Hermes Agent commited on
Commit
63d138a
·
1 Parent(s): 726f909

feat(ui): resolve top bar mounting across MUI toolbars, hide hamburger menu, pack 6-7 movie cards in landscape, and eliminate startup lag

Browse files
Files changed (2) hide show
  1. apex/ui/abyss.css +119 -35
  2. apex/ui/spotlight-loader.js +200 -203
apex/ui/abyss.css CHANGED
@@ -3846,7 +3846,14 @@ div[data-category="nextup"] .card,
3846
 
3847
  /* Top Floating Category Navigation (Android TV / Disney+ Style) */
3848
 
3849
- /* 1. COMPLETELY HIDE CAST, SYNCPLAY (two people icon), AND HAMBURGER MENU */
 
 
 
 
 
 
 
3850
  .mainDrawerButton,
3851
  .headerButtonMenu,
3852
  button[title*="menu" i],
@@ -3865,7 +3872,9 @@ button[aria-label*="Cast" i],
3865
  button:has(svg[data-testid="CastIcon"]),
3866
  button:has(svg[data-testid="GroupIcon"]),
3867
  button:has(svg[data-testid="PeopleIcon"]),
3868
- .headerLeft {
 
 
3869
  display: none !important;
3870
  visibility: hidden !important;
3871
  pointer-events: none !important;
@@ -3881,13 +3890,15 @@ button:has(svg[data-testid="PeopleIcon"]),
3881
  /* 2. HEADER CONTAINER & BAR */
3882
  .skinHeader,
3883
  .skinHeader.focuscontainer-x,
3884
- .skinHeader-withBackground {
3885
- background: linear-gradient(180deg, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.45) 75%, transparent 100%) !important;
 
3886
  backdrop-filter: blur(20px) !important;
3887
  -webkit-backdrop-filter: blur(20px) !important;
3888
  border-bottom: none !important;
3889
  box-shadow: none !important;
3890
  height: 56px !important;
 
3891
  padding: 0 !important;
3892
  position: fixed !important;
3893
  top: 0 !important;
@@ -3896,12 +3907,15 @@ button:has(svg[data-testid="PeopleIcon"]),
3896
  z-index: 1000 !important;
3897
  }
3898
 
3899
- .headerTop {
 
 
3900
  display: flex !important;
3901
  flex-direction: row !important;
3902
  justify-content: space-between !important;
3903
  align-items: center !important;
3904
- height: 100% !important;
 
3905
  width: 100% !important;
3906
  padding: 0 16px !important;
3907
  margin: 0 !important;
@@ -3918,6 +3932,7 @@ button:has(svg[data-testid="PeopleIcon"]),
3918
  padding: 0 !important;
3919
  z-index: 100 !important;
3920
  flex-shrink: 0 !important;
 
3921
  }
3922
 
3923
  .apex-nav-item {
@@ -3941,45 +3956,29 @@ button:has(svg[data-testid="PeopleIcon"]),
3941
  .apex-nav-item:focus-visible {
3942
  color: #FFFFFF !important;
3943
  background: rgba(255, 255, 255, 0.12) !important;
3944
- border-color: rgba(255, 255, 255, 0.18) !important;
3945
  }
3946
 
3947
  .apex-nav-item.active {
3948
  color: #FFFFFF !important;
3949
- font-weight: 700 !important;
3950
- background: rgba(255, 255, 255, 0.18) !important;
3951
- border-color: rgba(255, 255, 255, 0.25) !important;
3952
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35), 0 0 10px rgba(255, 255, 255, 0.1) !important;
3953
  }
3954
 
3955
- /* 4. RIGHT: HEADER RIGHT (Search and Profile) */
3956
- .headerRight {
 
 
3957
  display: flex !important;
3958
  flex-direction: row !important;
3959
  align-items: center !important;
3960
  gap: 10px !important;
3961
- margin: 0 !important;
3962
- padding: 0 !important;
3963
- flex-shrink: 0 !important;
3964
  }
3965
 
3966
- /* Profile Avatar Icon */
3967
- .headerRight .headerUserButtonRound,
3968
- .headerRight .headerButton-user {
3969
- border-radius: 50% !important;
3970
- width: 34px !important;
3971
- height: 34px !important;
3972
- min-width: 34px !important;
3973
- border: 1.5px solid rgba(255, 255, 255, 0.35) !important;
3974
- overflow: hidden !important;
3975
- padding: 0 !important;
3976
- margin: 0 !important;
3977
- display: flex !important;
3978
- align-items: center !important;
3979
- justify-content: center !important;
3980
- }
3981
-
3982
- /* Search button default */
3983
  .headerSearchButton,
3984
  button[data-action="search"] {
3985
  background: transparent !important;
@@ -3997,7 +3996,9 @@ button[data-action="search"] {
3997
 
3998
  /* Landscape Mode & Desktop/TV: EXACT MATCH TO IMAGE 4 */
3999
  @media (min-width: 601px), (orientation: landscape) {
4000
- .headerTop {
 
 
4001
  padding: 0 24px !important;
4002
  }
4003
 
@@ -4042,7 +4043,9 @@ button[data-action="search"] {
4042
 
4043
  /* Mobile Portrait Mode */
4044
  @media (max-width: 600px) and (orientation: portrait) {
4045
- .headerTop {
 
 
4046
  padding: 0 12px !important;
4047
  }
4048
 
@@ -4192,3 +4195,84 @@ div[data-category="nextup"] .cardScalable,
4192
  padding: 4px 2px 0 !important;
4193
  }
4194
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3846
 
3847
  /* Top Floating Category Navigation (Android TV / Disney+ Style) */
3848
 
3849
+ /* Top Floating Category Navigation (Android TV / Disney+ Style) */
3850
+
3851
+ /* 1. COMPLETELY HIDE HAMBURGER MENU, CAST, AND SYNCPLAY */
3852
+ button[aria-label="Menu"],
3853
+ button[aria-label="menu"],
3854
+ button:has(svg[data-testid="MenuIcon"]),
3855
+ svg[data-testid="MenuIcon"],
3856
+ .claude-stepped-menu,
3857
  .mainDrawerButton,
3858
  .headerButtonMenu,
3859
  button[title*="menu" i],
 
3872
  button:has(svg[data-testid="CastIcon"]),
3873
  button:has(svg[data-testid="GroupIcon"]),
3874
  button:has(svg[data-testid="PeopleIcon"]),
3875
+ .headerLeft,
3876
+ header.MuiAppBar-root > .MuiToolbar-root > .MuiBox-root:first-of-type:has(button[aria-label="Menu"]),
3877
+ header.MuiAppBar-root > .MuiToolbar-root > .MuiStack-root:first-of-type:has(button[aria-label="Menu"]) {
3878
  display: none !important;
3879
  visibility: hidden !important;
3880
  pointer-events: none !important;
 
3890
  /* 2. HEADER CONTAINER & BAR */
3891
  .skinHeader,
3892
  .skinHeader.focuscontainer-x,
3893
+ .skinHeader-withBackground,
3894
+ header.MuiAppBar-root {
3895
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.5) 75%, transparent 100%) !important;
3896
  backdrop-filter: blur(20px) !important;
3897
  -webkit-backdrop-filter: blur(20px) !important;
3898
  border-bottom: none !important;
3899
  box-shadow: none !important;
3900
  height: 56px !important;
3901
+ min-height: 56px !important;
3902
  padding: 0 !important;
3903
  position: fixed !important;
3904
  top: 0 !important;
 
3907
  z-index: 1000 !important;
3908
  }
3909
 
3910
+ .headerTop,
3911
+ header.MuiAppBar-root > .MuiToolbar-root,
3912
+ header > .MuiToolbar-root {
3913
  display: flex !important;
3914
  flex-direction: row !important;
3915
  justify-content: space-between !important;
3916
  align-items: center !important;
3917
+ height: 56px !important;
3918
+ min-height: 56px !important;
3919
  width: 100% !important;
3920
  padding: 0 16px !important;
3921
  margin: 0 !important;
 
3932
  padding: 0 !important;
3933
  z-index: 100 !important;
3934
  flex-shrink: 0 !important;
3935
+ order: -1 !important;
3936
  }
3937
 
3938
  .apex-nav-item {
 
3956
  .apex-nav-item:focus-visible {
3957
  color: #FFFFFF !important;
3958
  background: rgba(255, 255, 255, 0.12) !important;
3959
+ border-color: rgba(255, 255, 255, 0.2) !important;
3960
  }
3961
 
3962
  .apex-nav-item.active {
3963
  color: #FFFFFF !important;
3964
+ background: rgba(255, 255, 255, 0.22) !important;
3965
+ border-color: rgba(255, 255, 255, 0.45) !important;
3966
+ font-weight: 600 !important;
3967
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4) !important;
3968
  }
3969
 
3970
+ /* 4. RIGHT: SEARCH AND PROFILE ONLY */
3971
+ .headerRight,
3972
+ header.MuiAppBar-root > .MuiToolbar-root > .MuiBox-root:last-of-type,
3973
+ header.MuiAppBar-root > .MuiToolbar-root > .MuiStack-root:last-of-type {
3974
  display: flex !important;
3975
  flex-direction: row !important;
3976
  align-items: center !important;
3977
  gap: 10px !important;
3978
+ margin-left: auto !important;
3979
+ order: 10 !important;
 
3980
  }
3981
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3982
  .headerSearchButton,
3983
  button[data-action="search"] {
3984
  background: transparent !important;
 
3996
 
3997
  /* Landscape Mode & Desktop/TV: EXACT MATCH TO IMAGE 4 */
3998
  @media (min-width: 601px), (orientation: landscape) {
3999
+ .headerTop,
4000
+ header.MuiAppBar-root > .MuiToolbar-root,
4001
+ header > .MuiToolbar-root {
4002
  padding: 0 24px !important;
4003
  }
4004
 
 
4043
 
4044
  /* Mobile Portrait Mode */
4045
  @media (max-width: 600px) and (orientation: portrait) {
4046
+ .headerTop,
4047
+ header.MuiAppBar-root > .MuiToolbar-root,
4048
+ header > .MuiToolbar-root {
4049
  padding: 0 12px !important;
4050
  }
4051
 
 
4195
  padding: 4px 2px 0 !important;
4196
  }
4197
  }
4198
+
4199
+ /* 4. LANDSCAPE MODE & WIDESCREEN: HIGH DENSITY CARDS (6 to 7 Cards Per Row) */
4200
+ @media (orientation: landscape), (min-width: 601px) {
4201
+ #moviesPage:not(#indexPage) .itemsContainer,
4202
+ #tvShowsPage:not(#indexPage) .itemsContainer,
4203
+ #showsPage:not(#indexPage) .itemsContainer,
4204
+ #libraryPage:not(#indexPage) .itemsContainer,
4205
+ .view-movies:not(#indexPage) .itemsContainer,
4206
+ .view-shows:not(#indexPage) .itemsContainer {
4207
+ display: flex !important;
4208
+ flex-wrap: wrap !important;
4209
+ flex-direction: row !important;
4210
+ gap: 10px !important;
4211
+ padding: 12px 16px !important;
4212
+ box-sizing: border-box !important;
4213
+ justify-content: flex-start !important;
4214
+ }
4215
+
4216
+ #moviesPage:not(#indexPage) .card,
4217
+ #tvShowsPage:not(#indexPage) .card,
4218
+ #showsPage:not(#indexPage) .card,
4219
+ #libraryPage:not(#indexPage) .card,
4220
+ .view-movies:not(#indexPage) .card,
4221
+ .view-shows:not(#indexPage) .card {
4222
+ width: calc((100% - 50px) / 6) !important;
4223
+ max-width: calc((100% - 50px) / 6) !important;
4224
+ flex: 0 0 calc((100% - 50px) / 6) !important;
4225
+ margin: 0 0 12px 0 !important;
4226
+ box-sizing: border-box !important;
4227
+ }
4228
+
4229
+ #moviesPage:not(#indexPage) .cardText,
4230
+ #tvShowsPage:not(#indexPage) .cardText,
4231
+ #showsPage:not(#indexPage) .cardText {
4232
+ font-size: 0.8rem !important;
4233
+ line-height: 1.25 !important;
4234
+ padding: 4px 2px 0 !important;
4235
+ white-space: nowrap !important;
4236
+ overflow: hidden !important;
4237
+ text-overflow: ellipsis !important;
4238
+ }
4239
+ }
4240
+
4241
+ @media (orientation: landscape) and (min-width: 860px), (min-width: 1024px) {
4242
+ #moviesPage:not(#indexPage) .card,
4243
+ #tvShowsPage:not(#indexPage) .card,
4244
+ #showsPage:not(#indexPage) .card,
4245
+ #libraryPage:not(#indexPage) .card,
4246
+ .view-movies:not(#indexPage) .card,
4247
+ .view-shows:not(#indexPage) .card {
4248
+ width: calc((100% - 60px) / 7) !important;
4249
+ max-width: calc((100% - 60px) / 7) !important;
4250
+ flex: 0 0 calc((100% - 60px) / 7) !important;
4251
+ }
4252
+ }
4253
+
4254
+ @media (min-width: 1400px) {
4255
+ #moviesPage:not(#indexPage) .card,
4256
+ #tvShowsPage:not(#indexPage) .card,
4257
+ #showsPage:not(#indexPage) .card,
4258
+ #libraryPage:not(#indexPage) .card,
4259
+ .view-movies:not(#indexPage) .card,
4260
+ .view-shows:not(#indexPage) .card {
4261
+ width: calc((100% - 70px) / 8) !important;
4262
+ max-width: calc((100% - 70px) / 8) !important;
4263
+ flex: 0 0 calc((100% - 70px) / 8) !important;
4264
+ }
4265
+ }
4266
+
4267
+ /* ZERO STARTUP LAG: INSTANT VISIBILITY OF HOMEPAGE CONTENT */
4268
+ #homeTab,
4269
+ .homeSectionsContainer,
4270
+ #indexPage .sections,
4271
+ .verticalSection,
4272
+ .section0,
4273
+ .section1,
4274
+ .section2,
4275
+ .section3 {
4276
+ opacity: 1 !important;
4277
+ visibility: visible !important;
4278
+ }
apex/ui/spotlight-loader.js CHANGED
@@ -411,38 +411,15 @@
411
 
412
  function installClaudeMenuIcon() {
413
  safe(function () {
414
- var svgs = document.querySelectorAll(
415
- 'svg[data-testid="MenuIcon"], button[aria-label="Menu"] svg, button[aria-label="menu"] svg, .mainDrawerButton svg'
 
 
416
  );
417
- for (var i = 0; i < svgs.length; i++) {
418
- var svg = svgs[i];
419
- if (svg.getAttribute("data-claude-menu") === "true") continue;
420
- svg.setAttribute("data-claude-menu", "true");
421
- svg.classList.add("claude-stepped-menu");
422
- svg.setAttribute("viewBox", "0 0 20 15");
423
- svg.setAttribute("width", "20");
424
- svg.setAttribute("height", "15");
425
- svg.style.width = "20px";
426
- svg.style.height = "15px";
427
- svg.innerHTML =
428
- '<rect x="0" y="0.5" width="20" height="2" rx="1" fill="white"/>' +
429
- '<rect x="0" y="6.5" width="13.5" height="2" rx="1" fill="white"/>' +
430
- '<rect x="0" y="12.5" width="7.5" height="2" rx="1" fill="white"/>';
431
- }
432
-
433
- var classicBtns = document.querySelectorAll(
434
- ".mainDrawerButton:not(:has(svg)), .headerButtonMenu:not(:has(svg))"
435
- );
436
- for (var j = 0; j < classicBtns.length; j++) {
437
- var btn = classicBtns[j];
438
- if (btn.getAttribute("data-claude-menu") === "true") continue;
439
- btn.setAttribute("data-claude-menu", "true");
440
- btn.innerHTML =
441
- '<svg class="claude-stepped-menu" viewBox="0 0 20 15" width="20" height="15">' +
442
- '<rect x="0" y="0.5" width="20" height="2" rx="1" fill="white"/>' +
443
- '<rect x="0" y="6.5" width="13.5" height="2" rx="1" fill="white"/>' +
444
- '<rect x="0" y="12.5" width="7.5" height="2" rx="1" fill="white"/>' +
445
- '</svg>';
446
  }
447
  });
448
  }
@@ -607,206 +584,226 @@
607
  });
608
  }
609
 
610
- var libIds = { movies: null, tvshows: null };
 
 
 
 
 
 
611
 
612
- function discoverLibraries() {
613
- try {
614
- if (window.ApiClient && typeof window.ApiClient.getCurrentUserId === "function") {
615
- var uid = window.ApiClient.getCurrentUserId();
616
- window.ApiClient.getUserViews(uid).then(function (res) {
617
- if (res && res.Items) {
618
- res.Items.forEach(function (it) {
619
- var name = (it.Name || "").toLowerCase();
620
- var type = (it.CollectionType || "").toLowerCase();
621
- if (type === "movies" || name.indexOf("movie") !== -1) {
622
- libIds.movies = it.Id;
623
- }
624
- if (type === "tvshows" || name.indexOf("tv") !== -1 || name.indexOf("show") !== -1 || name.indexOf("series") !== -1) {
625
- libIds.tvshows = it.Id;
626
- }
627
- });
628
- }
629
- }).catch(function () {});
630
- }
631
- } catch (e) {}
632
- }
633
 
634
- function navigateCategory(name) {
635
- if (name === "Home") {
636
- if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
637
- window.Emby.Page.show("/home");
638
- } else {
639
- window.location.hash = "#/home";
640
- }
641
- window.scrollTo({ top: 0, behavior: "smooth" });
642
- return;
 
 
 
 
 
 
 
 
 
 
 
643
  }
 
 
644
 
645
- if (name === "Movies") {
646
- if (libIds.movies) {
647
- var url = "/details?id=" + libIds.movies;
648
- if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
649
- window.Emby.Page.show(url);
650
- return;
651
- }
652
- window.location.hash = "#" + url;
 
 
 
 
 
 
 
 
653
  return;
654
  }
655
- var cards = document.querySelectorAll('.card[data-type="CollectionFolder"], [data-category="views"] .card');
656
- for (var i = 0; i < cards.length; i++) {
657
- if ((cards[i].textContent || "").toLowerCase().indexOf("movie") !== -1) {
658
- cards[i].click();
659
- return;
660
- }
661
  }
662
- window.location.hash = "#/movies";
663
  return;
664
  }
 
 
 
665
 
666
- if (name === "TV Shows") {
667
- if (libIds.tvshows) {
668
- var url = "/details?id=" + libIds.tvshows;
669
- if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
670
- window.Emby.Page.show(url);
671
- return;
672
- }
673
- window.location.hash = "#" + url;
674
  return;
675
  }
676
- var tvCards = document.querySelectorAll('.card[data-type="CollectionFolder"], [data-category="views"] .card');
677
- for (var t = 0; t < tvCards.length; t++) {
678
- var text = (tvCards[t].textContent || "").toLowerCase();
679
- if (text.indexOf("tv") !== -1 || text.indexOf("show") !== -1 || text.indexOf("series") !== -1) {
680
- tvCards[t].click();
681
- return;
682
- }
683
  }
684
- window.location.hash = "#/shows";
685
  return;
686
  }
 
 
687
  }
 
688
 
689
- function updateActiveNavTab() {
690
- var hash = window.location.hash || "";
691
- var links = document.querySelectorAll(".apex-nav-item");
692
- for (var i = 0; i < links.length; i++) {
693
- var item = links[i];
694
- var navType = item.getAttribute("data-nav");
695
- var isActive = false;
696
- if (navType === "Home" && (!hash || hash.indexOf("/home") !== -1 || hash === "#/" || hash === "#")) {
697
- isActive = true;
698
- } else if (navType === "Movies" && (hash.indexOf("movie") !== -1 || (libIds.movies && hash.indexOf(libIds.movies) !== -1))) {
699
- isActive = true;
700
- } else if (navType === "TV Shows" && (hash.indexOf("show") !== -1 || hash.indexOf("series") !== -1 || hash.indexOf("tv") !== -1 || (libIds.tvshows && hash.indexOf(libIds.tvshows) !== -1))) {
701
- isActive = true;
702
- }
703
- if (isActive) item.classList.add("active");
704
- else item.classList.remove("active");
705
  }
 
 
706
  }
 
707
 
708
- function ensureTopNavigation() {
709
- safe(function () {
710
- var header = document.querySelector(".skinHeader, .skinHeader-withBackground, header");
711
- if (!header) return;
712
-
713
- discoverLibraries();
714
-
715
- var headerTop = header.querySelector(".headerTop") || header;
716
-
717
- // 1. Remove Cast, SyncPlay, and Drawer button completely
718
- var unwanted = headerTop.querySelectorAll(
719
- ".mainDrawerButton, .headerButtonMenu, .headerSyncplayButton, .btnSyncPlay, " +
720
- ".headerCastButton, .btnCast, button[data-action='cast'], button[data-action='syncplay'], " +
721
- "button[title*='SyncPlay' i], button[aria-label*='SyncPlay' i], button[title*='Cast' i], button[aria-label*='Cast' i]"
722
- );
723
- for (var u = 0; u < unwanted.length; u++) {
724
- unwanted[u].style.display = "none";
725
- unwanted[u].style.visibility = "hidden";
726
- unwanted[u].style.pointerEvents = "none";
727
- }
728
-
729
- // 2. Hide .headerLeft completely (top nav replaces it)
730
- var headerLeft = headerTop.querySelector(".headerLeft");
731
- if (headerLeft) headerLeft.style.display = "none";
732
-
733
- // 3. Mount or verify apex-top-nav
734
- var existingNav = headerTop.querySelector(".apex-top-nav");
735
- if (!existingNav) {
736
- var strays = document.querySelectorAll(".apex-top-nav");
737
- for (var s = 0; s < strays.length; s++) {
738
- if (strays[s].parentNode) strays[s].parentNode.removeChild(strays[s]);
739
- }
740
 
741
- var navContainer = document.createElement("div");
742
- navContainer.className = "apex-top-nav";
743
-
744
- var categories = [
745
- { label: "Home", name: "Home" },
746
- { label: "Movies", name: "Movies" },
747
- { label: "TV Shows", name: "TV Shows" }
748
- ];
749
-
750
- categories.forEach(function (cat) {
751
- var btn = document.createElement("button");
752
- btn.type = "button";
753
- btn.className = "apex-nav-item";
754
- btn.textContent = cat.label;
755
- btn.setAttribute("data-nav", cat.name);
756
- btn.addEventListener("click", function (e) {
757
- e.preventDefault();
758
- e.stopPropagation();
759
- var all = document.querySelectorAll(".apex-nav-item");
760
- for (var a = 0; a < all.length; a++) all[a].classList.remove("active");
761
- btn.classList.add("active");
762
- navigateCategory(cat.name);
763
- });
764
- navContainer.appendChild(btn);
765
- });
766
 
767
- headerTop.insertBefore(navContainer, headerTop.firstChild);
 
 
 
 
 
768
  }
769
 
770
- updateActiveNavTab();
771
-
772
- // 4. Ensure Search Pill in landscape & desktop
773
- var headerRight = headerTop.querySelector(".headerRight");
774
- if (headerRight && !headerRight.querySelector(".apex-search-pill")) {
775
- var searchPill = document.createElement("button");
776
- searchPill.type = "button";
777
- searchPill.className = "apex-search-pill";
778
- searchPill.setAttribute("aria-label", "Search");
779
- searchPill.innerHTML = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg><span>Search</span>';
780
- searchPill.addEventListener("click", function (e) {
 
 
 
 
 
781
  e.preventDefault();
782
  e.stopPropagation();
783
- var nativeSearch = document.querySelector('.headerSearchButton, [data-action="search"], button[title*="Search" i]');
784
- if (nativeSearch && nativeSearch.click) {
785
- nativeSearch.click();
786
- } else {
787
- window.location.hash = "#/search";
788
- }
789
  });
790
- var userBtn = headerRight.querySelector(".headerUserButtonRound, .headerButton-user");
791
- if (userBtn) {
792
- headerRight.insertBefore(searchPill, userBtn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
793
  } else {
794
- headerRight.insertBefore(searchPill, headerRight.firstChild);
795
  }
 
 
 
 
 
 
796
  }
797
- });
798
- }
 
799
 
800
- var topNavGuardianTimer = null;
801
- function startTopNavGuardian() {
802
- if (topNavGuardianTimer) clearInterval(topNavGuardianTimer);
803
- topNavGuardianTimer = setInterval(ensureTopNavigation, 400);
804
- window.addEventListener("hashchange", ensureTopNavigation);
805
- window.addEventListener("popstate", ensureTopNavigation);
806
- window.addEventListener("pageshow", ensureTopNavigation);
807
- document.addEventListener("viewshow", ensureTopNavigation);
808
- }
809
- startTopNavGuardian();
810
 
811
  var dpadInstalled = false;
812
  function installSpatialDpadNavigation() {
@@ -1060,7 +1057,7 @@
1060
  ensureTopNavigation();
1061
  replaceServerNameText();
1062
  installSpatialDpadNavigation();
1063
- enhanceLibraryCards();
1064
  };
1065
 
1066
  if (typeof requestAnimationFrame === "function") {
@@ -1076,7 +1073,7 @@
1076
  ensureTopNavigation();
1077
  replaceServerNameText();
1078
  installSpatialDpadNavigation();
1079
- enhanceLibraryCards();
1080
  window.addEventListener("hashchange", syncSettingsTheme);
1081
  if (typeof MutationObserver === "function") {
1082
  safe(function () {
@@ -1108,7 +1105,7 @@
1108
  ensureTopNavigation();
1109
  replaceServerNameText();
1110
  installSpatialDpadNavigation();
1111
- enhanceLibraryCards();
1112
  if (currentSync) safe(currentSync);
1113
  }, 2000);
1114
 
 
411
 
412
  function installClaudeMenuIcon() {
413
  safe(function () {
414
+ var btns = document.querySelectorAll(
415
+ 'button[aria-label="Menu"], button[aria-label="menu"], ' +
416
+ 'button:has(svg[data-testid="MenuIcon"]), svg[data-testid="MenuIcon"], ' +
417
+ '.claude-stepped-menu, .mainDrawerButton, .headerButtonMenu, .headerLeft'
418
  );
419
+ for (var i = 0; i < btns.length; i++) {
420
+ btns[i].style.display = "none";
421
+ btns[i].style.visibility = "hidden";
422
+ btns[i].style.pointerEvents = "none";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  }
424
  });
425
  }
 
584
  });
585
  }
586
 
587
+ var enhanceTimeout = null;
588
+ function debouncedEnhanceLibraryCards() {
589
+ if (enhanceTimeout) clearTimeout(enhanceTimeout);
590
+ enhanceTimeout = setTimeout(function () {
591
+ debouncedEnhanceLibraryCards();
592
+ }, 600);
593
+ }
594
 
595
+ var libIds = { movies: null, tvshows: null };
596
+ var librariesDiscovered = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
 
598
+ function discoverLibraries() {
599
+ if (librariesDiscovered) return;
600
+ try {
601
+ if (window.ApiClient && typeof window.ApiClient.getCurrentUserId === "function") {
602
+ var uid = window.ApiClient.getCurrentUserId();
603
+ if (!uid) return;
604
+ window.ApiClient.getUserViews(uid).then(function (res) {
605
+ if (res && res.Items) {
606
+ librariesDiscovered = true;
607
+ res.Items.forEach(function (it) {
608
+ var name = (it.Name || "").toLowerCase();
609
+ var type = (it.CollectionType || "").toLowerCase();
610
+ if (type === "movies" || name.indexOf("movie") !== -1) {
611
+ libIds.movies = it.Id;
612
+ } else if (type === "tvshows" || type === "series" || name.indexOf("show") !== -1 || name.indexOf("tv") !== -1 || name.indexOf("series") !== -1) {
613
+ libIds.tvshows = it.Id;
614
+ }
615
+ });
616
+ }
617
+ }).catch(function () {});
618
  }
619
+ } catch (e) {}
620
+ }
621
 
622
+ function navigateCategory(name) {
623
+ if (name === "Home") {
624
+ if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
625
+ window.Emby.Page.show("/home");
626
+ } else {
627
+ window.location.hash = "#/home";
628
+ }
629
+ window.scrollTo({ top: 0, behavior: "smooth" });
630
+ return;
631
+ }
632
+
633
+ if (name === "Movies") {
634
+ var cards = document.querySelectorAll('.card[data-type="CollectionFolder"], [data-category="views"] .card');
635
+ for (var i = 0; i < cards.length; i++) {
636
+ if ((cards[i].textContent || "").toLowerCase().indexOf("movie") !== -1) {
637
+ cards[i].click();
638
  return;
639
  }
640
+ }
641
+ if (libIds.movies) {
642
+ var url = "/details?id=" + libIds.movies;
643
+ if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
644
+ window.Emby.Page.show(url);
645
+ return;
646
  }
647
+ window.location.hash = "#" + url;
648
  return;
649
  }
650
+ window.location.hash = "#/movies";
651
+ return;
652
+ }
653
 
654
+ if (name === "TV Shows") {
655
+ var tvCards = document.querySelectorAll('.card[data-type="CollectionFolder"], [data-category="views"] .card');
656
+ for (var t = 0; t < tvCards.length; t++) {
657
+ var text = (tvCards[t].textContent || "").toLowerCase();
658
+ if (text.indexOf("tv") !== -1 || text.indexOf("show") !== -1 || text.indexOf("series") !== -1) {
659
+ tvCards[t].click();
 
 
660
  return;
661
  }
662
+ }
663
+ if (libIds.tvshows) {
664
+ var url = "/details?id=" + libIds.tvshows;
665
+ if (window.Emby && window.Emby.Page && typeof window.Emby.Page.show === "function") {
666
+ window.Emby.Page.show(url);
667
+ return;
 
668
  }
669
+ window.location.hash = "#" + url;
670
  return;
671
  }
672
+ window.location.hash = "#/shows";
673
+ return;
674
  }
675
+ }
676
 
677
+ function updateActiveNavTab() {
678
+ var hash = window.location.hash || "";
679
+ var links = document.querySelectorAll(".apex-nav-item");
680
+ for (var i = 0; i < links.length; i++) {
681
+ var item = links[i];
682
+ var navType = item.getAttribute("data-nav");
683
+ var isActive = false;
684
+ if (navType === "Home" && (!hash || hash.indexOf("/home") !== -1 || hash === "#/" || hash === "#")) {
685
+ isActive = true;
686
+ } else if (navType === "Movies" && (hash.indexOf("movie") !== -1 || (libIds.movies && hash.indexOf(libIds.movies) !== -1))) {
687
+ isActive = true;
688
+ } else if (navType === "TV Shows" && (hash.indexOf("show") !== -1 || hash.indexOf("series") !== -1 || hash.indexOf("tv") !== -1 || (libIds.tvshows && hash.indexOf(libIds.tvshows) !== -1))) {
689
+ isActive = true;
 
 
 
690
  }
691
+ if (isActive) item.classList.add("active");
692
+ else item.classList.remove("active");
693
  }
694
+ }
695
 
696
+ function findActiveToolbar() {
697
+ return document.querySelector(
698
+ "header.MuiAppBar-root > .MuiToolbar-root, " +
699
+ "header > .MuiToolbar-root, " +
700
+ ".skinHeader .headerTop, " +
701
+ ".headerTop, " +
702
+ "header.MuiAppBar-root, " +
703
+ ".skinHeader, " +
704
+ "header"
705
+ );
706
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
 
708
+ function ensureTopNavigation() {
709
+ safe(function () {
710
+ var bar = findActiveToolbar();
711
+ if (!bar) return;
712
+
713
+ discoverLibraries();
714
+
715
+ // 1. Hide Hamburger, Cast, SyncPlay, and stray drawer buttons completely
716
+ var unwanted = document.querySelectorAll(
717
+ 'button[aria-label="Menu"], button[aria-label="menu"], ' +
718
+ 'button:has(svg[data-testid="MenuIcon"]), svg[data-testid="MenuIcon"], .claude-stepped-menu, ' +
719
+ '.mainDrawerButton, .headerButtonMenu, .headerSyncplayButton, .btnSyncPlay, ' +
720
+ '.headerCastButton, .btnCast, button[data-action="cast"], button[data-action="syncplay"], ' +
721
+ 'button[title*="SyncPlay" i], button[aria-label*="SyncPlay" i], button[title*="Cast" i], button[aria-label*="Cast" i], ' +
722
+ '.headerLeft, .headerTabs.sectionTabs'
723
+ );
724
+ for (var u = 0; u < unwanted.length; u++) {
725
+ unwanted[u].style.display = "none";
726
+ unwanted[u].style.visibility = "hidden";
727
+ unwanted[u].style.pointerEvents = "none";
728
+ }
 
 
 
 
729
 
730
+ // 2. Mount or verify apex-top-nav inside the active toolbar
731
+ var existingNav = bar.querySelector(".apex-top-nav");
732
+ if (!existingNav) {
733
+ var strays = document.querySelectorAll(".apex-top-nav");
734
+ for (var s = 0; s < strays.length; s++) {
735
+ if (strays[s].parentNode) strays[s].parentNode.removeChild(strays[s]);
736
  }
737
 
738
+ var navContainer = document.createElement("div");
739
+ navContainer.className = "apex-top-nav";
740
+
741
+ var categories = [
742
+ { label: "Home", name: "Home" },
743
+ { label: "Movies", name: "Movies" },
744
+ { label: "TV Shows", name: "TV Shows" }
745
+ ];
746
+
747
+ categories.forEach(function (cat) {
748
+ var btn = document.createElement("button");
749
+ btn.type = "button";
750
+ btn.className = "apex-nav-item";
751
+ btn.textContent = cat.label;
752
+ btn.setAttribute("data-nav", cat.name);
753
+ btn.addEventListener("click", function (e) {
754
  e.preventDefault();
755
  e.stopPropagation();
756
+ var all = document.querySelectorAll(".apex-nav-item");
757
+ for (var a = 0; a < all.length; a++) all[a].classList.remove("active");
758
+ btn.classList.add("active");
759
+ navigateCategory(cat.name);
 
 
760
  });
761
+ navContainer.appendChild(btn);
762
+ });
763
+
764
+ bar.insertBefore(navContainer, bar.firstChild);
765
+ }
766
+
767
+ updateActiveNavTab();
768
+
769
+ // 3. Ensure Search Pill in landscape & desktop
770
+ var headerRight = bar.querySelector(".headerRight, .MuiBox-root:last-of-type, .MuiStack-root:last-of-type") || bar;
771
+ if (headerRight && !bar.querySelector(".apex-search-pill")) {
772
+ var searchPill = document.createElement("button");
773
+ searchPill.type = "button";
774
+ searchPill.className = "apex-search-pill";
775
+ searchPill.setAttribute("aria-label", "Search");
776
+ searchPill.innerHTML = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg><span>Search</span>';
777
+ searchPill.addEventListener("click", function (e) {
778
+ e.preventDefault();
779
+ e.stopPropagation();
780
+ var nativeSearch = document.querySelector('.headerSearchButton, [data-action="search"], button[title*="Search" i]');
781
+ if (nativeSearch && nativeSearch.click) {
782
+ nativeSearch.click();
783
  } else {
784
+ window.location.hash = "#/search";
785
  }
786
+ });
787
+ var userBtn = headerRight.querySelector(".headerUserButtonRound, .headerButton-user, button[aria-label*='user' i], button[aria-label*='profile' i]");
788
+ if (userBtn) {
789
+ headerRight.insertBefore(searchPill, userBtn);
790
+ } else {
791
+ headerRight.insertBefore(searchPill, headerRight.firstChild);
792
  }
793
+ }
794
+ });
795
+ }
796
 
797
+ var topNavGuardianTimer = null;
798
+ function startTopNavGuardian() {
799
+ if (topNavGuardianTimer) clearInterval(topNavGuardianTimer);
800
+ topNavGuardianTimer = setInterval(ensureTopNavigation, 400);
801
+ window.addEventListener("hashchange", ensureTopNavigation);
802
+ window.addEventListener("popstate", ensureTopNavigation);
803
+ window.addEventListener("pageshow", ensureTopNavigation);
804
+ document.addEventListener("viewshow", ensureTopNavigation);
805
+ }
806
+ startTopNavGuardian();
807
 
808
  var dpadInstalled = false;
809
  function installSpatialDpadNavigation() {
 
1057
  ensureTopNavigation();
1058
  replaceServerNameText();
1059
  installSpatialDpadNavigation();
1060
+ debouncedEnhanceLibraryCards();
1061
  };
1062
 
1063
  if (typeof requestAnimationFrame === "function") {
 
1073
  ensureTopNavigation();
1074
  replaceServerNameText();
1075
  installSpatialDpadNavigation();
1076
+ debouncedEnhanceLibraryCards();
1077
  window.addEventListener("hashchange", syncSettingsTheme);
1078
  if (typeof MutationObserver === "function") {
1079
  safe(function () {
 
1105
  ensureTopNavigation();
1106
  replaceServerNameText();
1107
  installSpatialDpadNavigation();
1108
+ debouncedEnhanceLibraryCards();
1109
  if (currentSync) safe(currentSync);
1110
  }, 2000);
1111