diff --git a/plugins/clean-view-luna/src/Settings.tsx b/plugins/clean-view-luna/src/Settings.tsx
new file mode 100644
index 0000000..7298b20
--- /dev/null
+++ b/plugins/clean-view-luna/src/Settings.tsx
@@ -0,0 +1,40 @@
+import { ReactiveStore } from "@luna/core";
+import { LunaSettings, LunaSwitchSetting } from "@luna/ui";
+import React from "react";
+
+export const settings = await ReactiveStore.getPluginStorage("CleanView", {
+ hideUIEnabled: false,
+ playerBarVisible: true,
+});
+
+export const Settings = () => {
+ const [hideUIEnabled, setHideUIEnabled] = React.useState(settings.hideUIEnabled);
+ const [playerBarVisible, setPlayerBarVisible] = React.useState(settings.playerBarVisible);
+
+ return (
+
+ {
+ console.log("Hide UI Feature:", checked ? "enabled" : "disabled");
+ setHideUIEnabled((settings.hideUIEnabled = checked));
+ }}
+ />
+ {
+ console.log("Player Bar Visibility:", checked ? "visible" : "hidden");
+ setPlayerBarVisible((settings.playerBarVisible = checked));
+ // Update styles immediately when setting changes
+ if ((window as any).updateCleanViewStyles) {
+ (window as any).updateCleanViewStyles();
+ }
+ }}
+ />
+
+ );
+};
\ No newline at end of file
diff --git a/plugins/clean-view-luna/src/index.ts b/plugins/clean-view-luna/src/index.ts
index 3701005..7057710 100644
--- a/plugins/clean-view-luna/src/index.ts
+++ b/plugins/clean-view-luna/src/index.ts
@@ -1,7 +1,8 @@
import { LunaUnload, Tracer, ftch } from "@luna/core";
+import { settings, Settings } from "./Settings";
export const { trace } = Tracer("[Clean View]");
-
+export { Settings };
// clean up resources
export const unloads = new Set();
@@ -25,6 +26,15 @@ const styles = `
margin: -40px;
}
+/* Only prevent specific text elements in player bar from being affected by margin adjustments */
+[data-test="footer-player"] [class*="_trackTitle"],
+[data-test="footer-player"] [class*="_artistName"],
+[data-test="footer-player"] [class*="_trackInfo"],
+[data-test="footer-player"] [class*="_trackContainer"] {
+ margin-top: 0 !important;
+ transform: none !important;
+}
+
[class*="_nowPlayingContainer"] {
padding-left: 6%;
padding-top: 20px !important;
@@ -153,13 +163,54 @@ const updateButtonStates = function(): void {
const unhideButton = document.querySelector('.unhide-ui-button') as HTMLElement;
if (hideButton) {
- hideButton.style.display = isCleanView ? 'none' : 'flex';
+ hideButton.style.display = (settings.hideUIEnabled && !isCleanView) ? 'flex' : 'none';
}
if (unhideButton) {
- unhideButton.style.display = isCleanView ? 'flex' : 'none';
+ unhideButton.style.display = (settings.hideUIEnabled && isCleanView) ? 'flex' : 'none';
}
};
+// Function to get dynamic styles including player bar visibility
+const getDynamicStyles = function(): string {
+ let dynamicStyles = styles;
+
+ // Add player bar hiding with hover reveal if setting is disabled
+ if (!settings.playerBarVisible) {
+ dynamicStyles += `
+/* Hide player bar when setting is disabled, but show on hover */
+[data-test="footer-player"] {
+ opacity: 0 !important;
+ transition: opacity 0.3s ease-in-out !important;
+ pointer-events: auto !important;
+}
+
+[data-test="footer-player"]:hover {
+ opacity: 1 !important;
+}
+
+/* Also show player bar when hovering over the bottom area */
+body:has([data-test="footer-player"]:hover) [data-test="footer-player"],
+body [data-test="footer-player"]:hover {
+ opacity: 1 !important;
+}
+`;
+ }
+
+ return dynamicStyles;
+};
+
+// Function to update styles when settings change
+const updateCleanViewStyles = function(): void {
+ if (isCleanView && appliedStyle) {
+ // Remove old styles and apply new ones with updated settings
+ appliedStyle.remove();
+ appliedStyle = ApplyCSS(getDynamicStyles());
+ }
+};
+
+// Make this function available globally so Settings can call it
+(window as any).updateCleanViewStyles = updateCleanViewStyles;
+
const toggleCleanView = function(): void {
if (isCleanView) {
if (appliedStyle) {
@@ -167,7 +218,7 @@ const toggleCleanView = function(): void {
appliedStyle = null;
}
} else {
- appliedStyle = ApplyCSS(styles);
+ appliedStyle = ApplyCSS(getDynamicStyles());
// Debug: Log bottom-left buttons to help identify selectors
//debugBottomLeftButtons();
}
@@ -194,6 +245,9 @@ const toggleCleanView = function(): void {
const createHideUIButton = function(): void {
setTimeout(() => {
+ // Only create button if Hide UI is enabled in settings
+ if (!settings.hideUIEnabled) return;
+
// Look for the fullscreen button's parent container
const fullscreenButton = document.querySelector('[data-test="request-fullscreen"]');
if (!fullscreenButton || !fullscreenButton.parentElement) {
@@ -254,6 +308,9 @@ const createHideUIButton = function(): void {
const createUnhideUIButton = function(): void {
setTimeout(() => {
+ // Only create button if Hide UI is enabled in settings
+ if (!settings.hideUIEnabled) return;
+
// Check if our button already exists
if (document.querySelector('.unhide-ui-button')) return;
@@ -358,14 +415,17 @@ function observeTrackTitle(): void {
// Also observe for the buttons to appear so we can add our buttons
function observeForButtons(): void {
const observer = new MutationObserver(() => {
- const fullscreenButton = document.querySelector('[data-test="request-fullscreen"]');
- if (fullscreenButton && !document.querySelector('.hide-ui-button')) {
- createHideUIButton();
- }
-
- // Create unhide button if it doesn't exist
- if (!document.querySelector('.unhide-ui-button')) {
- createUnhideUIButton();
+ // Only observe for buttons if Hide UI is enabled
+ if (settings.hideUIEnabled) {
+ const fullscreenButton = document.querySelector('[data-test="request-fullscreen"]');
+ if (fullscreenButton && !document.querySelector('.hide-ui-button')) {
+ createHideUIButton();
+ }
+
+ // Create unhide button if it doesn't exist
+ if (!document.querySelector('.unhide-ui-button')) {
+ createUnhideUIButton();
+ }
}
});
diff --git a/plugins/oled-theme-luna/src/Settings.tsx b/plugins/oled-theme-luna/src/Settings.tsx
new file mode 100644
index 0000000..bb7fae6
--- /dev/null
+++ b/plugins/oled-theme-luna/src/Settings.tsx
@@ -0,0 +1,44 @@
+import { ReactiveStore } from "@luna/core";
+import { LunaSettings, LunaSwitchSetting } from "@luna/ui";
+import React from "react";
+
+export const settings = await ReactiveStore.getPluginStorage("OLEDTheme", {
+ qualityColorMatchedSeekBar: true,
+ oledFriendlyButtons: true,
+});
+
+export const Settings = () => {
+ const [qualityColorMatchedSeekBar, setQualityColorMatchedSeekBar] = React.useState(settings.qualityColorMatchedSeekBar);
+ const [oledFriendlyButtons, setOledFriendlyButtons] = React.useState(settings.oledFriendlyButtons);
+
+ return (
+
+ {
+ console.log("Quality Color Matched Seek Bar:", checked ? "enabled" : "disabled");
+ setQualityColorMatchedSeekBar((settings.qualityColorMatchedSeekBar = checked));
+ // Update styles immediately when setting changes
+ if ((window as any).updateOLEDThemeStyles) {
+ (window as any).updateOLEDThemeStyles();
+ }
+ }}
+ />
+ {
+ console.log("OLED Friendly Buttons:", checked ? "enabled" : "disabled");
+ setOledFriendlyButtons((settings.oledFriendlyButtons = checked));
+ // Update styles immediately when setting changes
+ if ((window as any).updateOLEDThemeStyles) {
+ (window as any).updateOLEDThemeStyles();
+ }
+ }}
+ />
+
+ );
+};
\ No newline at end of file
diff --git a/plugins/oled-theme-luna/src/index.css b/plugins/oled-theme-luna/src/index.css
deleted file mode 100644
index 5bd6c99..0000000
--- a/plugins/oled-theme-luna/src/index.css
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
-{
- "name": "Abyss Neptune",
- "author": "@itzzexcel",
- "description": "Abyss Neptune: ShadowX Theme from Spicetify to TIDAL (17/Jan/2025)"
-}
-*/
-
-::-webkit-scrollbar {
- display: none;
-}
-
-:root {
- --wave-color-solid-accent-fill: white;
- --wave-color-solid-rainbow-yellow-fill: white;
- --wave-color-solid-contrast-fill: white;
- --wave-color-solid-base-brighter: black;
- --wave-text-body-medium: white !important;
- --track-vibrant-color: white !important;
- --wave-color-opacity-contrast-fill-ultra-thin: #fffafa1a !important;
- --wave-color-solid-rainbow-yellow-darkest: #fffafa1a !important;
- --wave-color-solid-accent-dark: rgb(128, 128, 128);
-}
-
-/* Credits to https://github.com/surfbryce for the fonts */
-@font-face {
- font-family: "AbyssFont";
- font-weight: 400;
- src: url("https://excel.lexploits.top/extra/tidal/LyricsRegular.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "AbyssFont";
- font-weight: 500;
- src: url("https://excel.lexploits.top/extra/tidal/LyricsMedium.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "AbyssFont";
- font-weight: 600;
- src: url("https://excel.lexploits.top/extra/tidal/LyricsSemibold.woff2") format("woff2");
-}
-
-@font-face {
- font-family: "AbyssFont";
- font-weight: 700;
- src: url("https://excel.lexploits.top/extra/tidal/LyricsBold.woff2") format("woff2");
-}
-
-[class^="followingButton"],
-[title="Unfollow"],
-[title="Follow"],
-[title="Unfollow"]>span,
-[title="Follow"]>span {
- background-color: var(--wave-color-solid-rainbow-yellow-fill) !important;
- color: var(--wave-color-solid-base-brighter);
-}
-
-[class^="_wave-badge-color-max"] {
- color: black !important;
- background-color: var(--wave-color-solid-accent-fill);
- border-radius: 3px;
-}
-
-[data-test="main-layout-sidebar-wrapper"] {
- border-right: rgb(25, 25, 25) 1px solid;
-}
-
-[class^="_wave-badge"] {
- background-color: var(--wave-color-solid-accent-fill);
- border-radius: 4px;
- color: black;
-}
-
-[class^="_progressBarWrapper"] {
- color: var(--wave-color-solid-accent-fill) !important;
-}
-
-[class^="_sidebarItem"]>span {
- color: var(--wave-color-solid-accent-dark);
-}
-
-[data-test="main-layout-header"] {
- border-left: 0 !important;
-}
-
-[class^="_sidebarItem"]:hover span {
- color: var(--wave-color-solid-contrast-fill);
-}
-
-[class^="_sidebarItem"] [class^="active"]>span {
- color: var(--wave-color-solid-accent-dark) !important;
-}
-
-[class^="_active"] {
- color: var(--wave-color-solid-accent-fill) !important;
-}
-
-[class^="ReactVirtualized__Grid"] {
- border-radius: 10px;
- margin: 5px;
-}
-
-[data-test="media-table"]>div>div>div {
- border: 1px solid rgb(25, 25, 25) !important;
-}
-
-[class^="ReactVirtualized__Grid__innerScrollContainer"] {
- border: none;
- margin: 5px;
-}
-
-[class^="button"]>span {
- color: black;
-}
-
-[class^="_explicitBadge"] {
- color: var(--wave-color-solid-accent-fill);
-}
-
-[class^="viewAllButton"] {
- border-radius: 4px;
- display: grid;
- place-items: center;
-}
-
-[data-test="current-media-imagery"] {
- border: 0 !important;
- margin: none;
-}
-
-[class^="_imageBorder"] {
- display: none;
-}
-
-[class^="_headerButtons"]>button,
-[class^="_headerButtons"]>button>span,
-[data-test="toggle-picture-in-picture"] {
- background-color: var(--wave-color-solid-accent-fill) !important;
- color: black;
-}
-
-[class^="_container"]>[class^="_navigationArrows"] {
- color: black;
- background-color: var(--wave-color-solid-accent-fill) !important;
- border-radius: 4px;
-}
-
-[class^="_buttons"]>button>span {
- color: black !important;
-}
-
-[class^="_container"]>button {
- border: 0px none;
-}
-
-
-[data-test="feed-sidebar"] {
- margin-top: 10px;
-}
-
-[data-test="footer-player"] {
- width: calc(100% - 20px);
- bottom: 10px;
- left: 10px;
- border: 1px solid rgb(25, 25, 25);
- border-radius: 4px !important;
- position: absolute !important;
-}
-
-[class^="_tooltipContainer"]>button {
- background-color: var(--wave-color-solid-accent-fill);
- color: black;
-}
-
-[class^="_tooltipContainer"]>button:hover {
- background-color: lightgray !important;
-}
-
-[class^="_tableRow"]:hover>*,
-[data-test-is-playing="true"]>* {
- color: var(--wave-color-solid-accent-fill) !important;
-}
-
-[class^="_tableRow"]>*,
-[data-test-is-playing="false"]>* {
- color: lightgray !important;
-}
-
-[class*="coverColumn"] {
- padding-left: 5px !important;
-}
-
-[class^="actionList"] {
- background-color: transparent;
- margin: 0px;
- border-radius: 5px;
-}
-
-button[data-test="request-fullscreen"],
-button[data-test="close-now-playing"],
-button[data-test="play-all"],
-button[data-test="shuffle-all"] {
- color: black;
- background-color: var(--wave-color-solid-accent-fill);
- border-radius: 12px;
-}
-
-button[data-test="request-fullscreen"]:hover,
-button[data-test="close-now-playing"]:hover {
- color: black;
- background-color: lightgray !important;
-}
-
-.neptune-switch-checkbox:checked+.neptune-switch {
- background-color: rgba(255, 255, 255, 0.1);
-}
-
-[data-test="navigation-arrows"]>button {
- background-color: var(--wave-color-solid-accent-fill) !important;
- color: black !important;
- border-radius: 5px;
-}
-
-[data-test="navigation-arrows"]>button:disabled {
- background-color: lightgray !important;
- opacity: 1;
-}
-
-[data-test="main-layout-header"],
-[data-test="feed-sidebar"],
-[data-test="stream-metadata"],
-[data-test="footer-player"] {
- background-color: rgba(0, 0, 0, 0.8) !important;
- backdrop-filter: blur(10px);
- border: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin) !important;
-}
-
-[data-wave-color=textUrl] {
- color: var(--wave-color-solid-accent-fill);
-}
-
-[class^="_smallHeader"] {
- margin-top: 7.5px;
-}
-
-[data-test="play-all"]>div>*,
-[data-test="shuffle-all"]>div>*,
-[data-test="play-all"],
-[data-test="shuffle-all"] {
- color: var(--wave-color-solid-accent-fill) !important;
- background-color: transparent !important;
-}
-
-[class^="__NEPTUNE_PAGE"],
-[data-test="main"] {
- margin-top: 35px;
-}
-
-[data-test="button-desktop-release-notes"],
-[data-test="button-release-notes"] {
- background-color: white;
-}
-
-[data-test="button-desktop-release-notes"]:hover,
-[data-test="button-release-notes"]:hover {
- background-color: lightgray !important;
- transition: none !important;
-}
-
-#playQueueSidebar {
- top: 50px !important;
- border: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin);
- margin: 2px;
- margin-right: -14px !important;
- background-color: rgba(0, 0, 0, 0.8) !important;
- backdrop-filter: blur(10px);
-}
-
-[class^="_bottomGradient"] {
- visibility: hidden;
-}
-
-[data-test="settings-page"] {
- padding-bottom: 60px !important;
-}
-
-[data-test="query-suggestions"],
-[data-test="recent-searches-container"] {
- background-color: rgba(0, 0, 0, 0.6);
- backdrop-filter: blur(10px);
-}
-
-[data-test="contextmenu"] {
- border: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin) !important;
-}
-
-[class^="_dataContainer_"]::before {
- background-image: var(--img);
- filter: blur(10px) brightness(0.4);
-}
\ No newline at end of file
diff --git a/plugins/oled-theme-luna/src/index.ts b/plugins/oled-theme-luna/src/index.ts
index 9c3573b..6f2f45a 100644
--- a/plugins/oled-theme-luna/src/index.ts
+++ b/plugins/oled-theme-luna/src/index.ts
@@ -1,6 +1,8 @@
import { LunaUnload, Tracer, ftch } from "@luna/core";
+import { settings, Settings } from "./Settings";
export const { trace } = Tracer("[OLED Theme]");
+export { Settings };
const themeUrl = "https://raw.githubusercontent.com/ItzzExcel/neptune-projects/refs/heads/main/themes/black-neptune-theme.css";
@@ -8,23 +10,110 @@ const themeUrl = "https://raw.githubusercontent.com/ItzzExcel/neptune-projects/r
// clean up resources
export const unloads = new Set();
+let originalStyle: string | null = null;
+let appliedStyleElement: HTMLStyleElement | null = null;
+
+// Function to apply theme styles based on current settings
+const applyThemeStyles = function(): void {
+ if (!originalStyle) return;
+
+ // Remove existing style element if it exists
+ if (appliedStyleElement && appliedStyleElement.parentNode) {
+ appliedStyleElement.parentNode.removeChild(appliedStyleElement);
+ }
+
+ let modifiedStyle = originalStyle;
+
+ // Remove SeekBar coloring if Quality Color Matched Seek Bar is enabled
+ if (settings.qualityColorMatchedSeekBar) {
+ modifiedStyle = modifiedStyle.replace(/\[class\^="_progressBarWrapper"\]\s*\{[^}]*\}/g, '');
+ trace.msg.log("OLED theme applied with SeekBar coloring removed");
+ } else {
+ trace.msg.log("OLED theme applied with original SeekBar coloring");
+ }
+
+ // Remove button styling if OLED Friendly Buttons is enabled
+ if (settings.oledFriendlyButtons) {
+ // First, let's debug what we're working with
+ const originalRuleCount = (modifiedStyle.match(/\{[^}]*\}/g) || []).length;
+ trace.msg.log(`Original CSS has ${originalRuleCount} rules`);
+
+ // Split CSS into individual rules and filter out button-related ones
+ const cssRules = modifiedStyle.split('}').filter(rule => rule.trim());
+ const filteredRules = cssRules.filter(rule => {
+ const lowerRule = rule.toLowerCase();
+
+ // Check if this rule might affect buttons
+ const isButtonRule =
+ // Direct button selectors
+ lowerRule.includes('button') ||
+ lowerRule.includes('[role="button"]') ||
+ lowerRule.includes('[type="button"]') ||
+ lowerRule.includes('[type="submit"]') ||
+ lowerRule.includes('[type="reset"]') ||
+ // Class-based button selectors
+ lowerRule.includes('btn') ||
+ lowerRule.includes('_button') ||
+ lowerRule.includes('-button') ||
+ lowerRule.includes('button-') ||
+ lowerRule.includes('button_') ||
+ // Common button-related classes
+ lowerRule.includes('clickable') ||
+ lowerRule.includes('action') ||
+ lowerRule.includes('control') ||
+ lowerRule.includes('icon') ||
+ // Data attributes that might be buttons
+ lowerRule.includes('data-test') && lowerRule.includes('button') ||
+ lowerRule.includes('aria-label') && lowerRule.includes('button') ||
+ // Button states
+ lowerRule.includes(':hover') && (lowerRule.includes('button') || lowerRule.includes('btn')) ||
+ lowerRule.includes(':focus') && (lowerRule.includes('button') || lowerRule.includes('btn')) ||
+ lowerRule.includes(':active') && (lowerRule.includes('button') || lowerRule.includes('btn')) ||
+ // Cursor pointer (often used on buttons)
+ lowerRule.includes('cursor') && lowerRule.includes('pointer') ||
+ // Any class containing button-like patterns
+ /\[class[^=]*=["'][^"']*button/i.test(rule) ||
+ /\[class[^=]*=["'][^"']*btn/i.test(rule) ||
+ /\[class[^=]*=["'][^"']*click/i.test(rule) ||
+ /\[class[^=]*=["'][^"']*action/i.test(rule) ||
+ /\[class[^=]*=["'][^"']*control/i.test(rule);
+
+ // Return true to keep the rule (i.e., if it's NOT a button rule)
+ return !isButtonRule;
+ });
+
+ modifiedStyle = filteredRules.join('} ') + (filteredRules.length > 0 ? '}' : '');
+
+ const filteredRuleCount = (modifiedStyle.match(/\{[^}]*\}/g) || []).length;
+ const removedRuleCount = originalRuleCount - filteredRuleCount;
+ trace.msg.log(`OLED Friendly Buttons enabled: Removed ${removedRuleCount} button-related CSS rules, ${filteredRuleCount} rules remaining`);
+ } else {
+ trace.msg.log("OLED Friendly Buttons disabled: Button styling preserved from original theme");
+ }
+
+ appliedStyleElement = document.createElement("style");
+ appliedStyleElement.type = "text/css";
+ appliedStyleElement.textContent = modifiedStyle;
+ document.head.appendChild(appliedStyleElement);
+};
+
+// Make this function available globally so Settings can call it
+(window as any).updateOLEDThemeStyles = applyThemeStyles;
+
// Added Top-level async since Luna plugins are modules <3
-const style = await ftch.text(themeUrl).catch((error: Error) => {
+originalStyle = await ftch.text(themeUrl).catch((error: Error) => {
trace.msg.err(`Failed to fetch theme CSS: ${error.message}`);
return null;
});
// Apply the OLED theme if CSS was fetched successfully
-if (style) {
- const styleElement = document.createElement("style");
- styleElement.type = "text/css";
- styleElement.textContent = style;
- document.head.appendChild(styleElement);
+if (originalStyle) {
+ applyThemeStyles();
// Add cleanup to unloads
unloads.add(() => {
- if (styleElement.parentNode) {
- styleElement.parentNode.removeChild(styleElement);
+ if (appliedStyleElement && appliedStyleElement.parentNode) {
+ appliedStyleElement.parentNode.removeChild(appliedStyleElement);
}
});
}
\ No newline at end of file
diff --git a/plugins/oled-theme-luna/src/types.d.ts b/plugins/oled-theme-luna/src/types.d.ts
deleted file mode 100644
index 72a2c2c..0000000
--- a/plugins/oled-theme-luna/src/types.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-declare module "*.css" {
- const content: string;
- export default content;
-}
\ No newline at end of file