+ ${buildToggleCard(
+ 'wl-side-menu',
+ config.sideMenu,
+ 'Side menu',
+ 'Show a Watchlist link in the left navigation drawer.'
+ )}
+ ${buildToggleCard(
+ 'wl-top-navigation',
+ config.topNavigation,
+ 'Top navigation',
+ 'Show Watchlist in the main top navigation bar (or via Custom Tabs when available).'
+ )}
+ ${buildToggleCard(
+ 'wl-user-menu',
+ config.userMenu,
+ 'User menu',
+ 'Show a Watchlist link in the user settings menu.'
+ )}
+
+ `;
+ }
+
+ function collectConfig() {
+ const side = document.getElementById('wl-side-menu');
+ const top = document.getElementById('wl-top-navigation');
+ const user = document.getElementById('wl-user-menu');
+ return {
+ sideMenu: side ? side.checked : DEFAULTS.sideMenu,
+ topNavigation: top ? top.checked : DEFAULTS.topNavigation,
+ userMenu: user ? user.checked : DEFAULTS.userMenu
+ };
+ }
+
+ function attachToggleHandlers(root) {
+ if (typeof window.KefinTweaksUI?.bindToggleCards === 'function') {
+ window.KefinTweaksUI.bindToggleCards(root);
+ return;
+ }
+ root.querySelectorAll('.kefin-toggle-switch').forEach((btn) => {
+ if (btn.dataset.kefinToggleBound === 'true') return;
+ btn.dataset.kefinToggleBound = 'true';
+ btn.addEventListener('click', () => {
+ const next = btn.dataset.enabled !== 'true';
+ btn.dataset.enabled = String(next);
+ btn.setAttribute('aria-pressed', String(next));
+ const checkboxId = btn.dataset.checkboxId;
+ const checkbox = checkboxId ? document.getElementById(checkboxId) : null;
+ if (checkbox) checkbox.checked = next;
+ });
+ });
+ }
+
+ async function openConfigModal() {
+ try {
+ const config = await loadConfig();
+ const content = document.createElement('div');
+ content.innerHTML = buildConfigHTML(config);
+
+ const footer = document.createElement('div');
+ footer.style.display = 'flex';
+ footer.style.gap = '0.75em';
+ footer.style.justifyContent = 'flex-end';
+ footer.innerHTML = `
+
+
+ `;
+
+ const modalInstance = window.ModalSystem.create({
+ id: MODAL_ID,
+ title: 'Watchlist',
+ content,
+ footer,
+ closeOnBackdrop: true,
+ closeOnEscape: true,
+ showCloseButton: true,
+ onOpen: (modal) => {
+ attachToggleHandlers(modal.dialogContent || content);
+ const saveBtn = modal.dialogFooter.querySelector('#save-watchlist-config-btn');
+ if (saveBtn) {
+ saveBtn.addEventListener('click', async () => {
+ const next = collectConfig();
+ const success = await saveConfig(next);
+ if (success) {
+ window.KefinTweaksToaster?.toast?.('Watchlist configuration saved!');
+ window.ModalSystem.close(MODAL_ID);
+ } else {
+ alert('Error saving configuration. Please ensure the JavaScript Injector plugin is installed and you have administrator permissions.');
+ }
+ });
+ }
+ }
+ });
+
+ if (modalInstance?.dialog) {
+ modalInstance.dialog.style.maxWidth = '90vw';
+ modalInstance.dialog.style.width = '560px';
+ }
+ LOG('Watchlist configuration modal opened');
+ } catch (error) {
+ ERR('Error opening Watchlist config modal:', error);
+ alert('Error opening configuration: ' + error.message);
+ }
+ }
+
+ window.KefinTweaksFeatureConfigs = window.KefinTweaksFeatureConfigs || {};
+ window.KefinTweaksFeatureConfigs.watchlist = { openConfigModal, DEFAULTS, normalizeConfig };
+
+ console.log('[KefinTweaks Watchlist Config] Script loaded');
+})();
diff --git a/scripts/configuration.js b/scripts/configuration.js
index 2ec530b..1ccfeb3 100644
--- a/scripts/configuration.js
+++ b/scripts/configuration.js
@@ -1177,7 +1177,7 @@
// Build script toggle switches
function buildScriptToggles(scripts) {
// Features that have configuration options (not just toggles)
- const featuresWithConfig = new Set(['homeScreen', 'search', 'flattenSingleSeasonShows', 'skinManager', 'customMenuLinks', 'seriesInfo', 'versionPreferences', 'thumbnailScrubber', 'watchTogether', 'userManager']);
+ const featuresWithConfig = new Set(['homeScreen', 'search', 'flattenSingleSeasonShows', 'skinManager', 'customMenuLinks', 'seriesInfo', 'versionPreferences', 'thumbnailScrubber', 'watchTogether', 'watchlist', 'userManager']);
const scriptNames = [
{ key: 'watchlist', label: 'Watchlist', desc: 'Allows your users to add items to their Watchlist. The Watchlist page shows an overview of all items on a user\'s Watchlist, as well as their Series Progress and Movie History. It also includes a Statistics page with an overview of your user watched stats.' },
diff --git a/scripts/watchlist.js b/scripts/watchlist.js
index 0af9596..31110fa 100644
--- a/scripts/watchlist.js
+++ b/scripts/watchlist.js
@@ -7092,7 +7092,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
});
function checkForEmptyWatchlist() {
- const onWatchlistPage = window.location.pathname.includes(window.KefinTweaksUtils._watchlistUrl || '#/watchlist');
+ const onWatchlistPage = window.location.pathname.includes(window.KefinTweaksUtils._watchlistUrl) || window.location.hash.includes(window.KefinTweaksUtils._watchlistUrl);
if (!onWatchlistPage) {
return;
@@ -7398,9 +7398,8 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
// Optional onViewPage backup (primary mount path is MutationObserver + addCustomPage)
if (window.KefinTweaksUtils && window.KefinTweaksUtils.onViewPage) {
window.KefinTweaksUtils.onViewPage(async (view, element, hash) => {
- const path = String(hash || '').split('?')[0];
const watchlistPath = window.KefinTweaksUtils._watchlistUrl || '#/watchlist';
- if (path.includes(watchlistPath)) {
+ if (hash.includes(watchlistPath)) {
LOG('onViewPage: Watchlist');
renderWatchlist();
}
@@ -7412,13 +7411,14 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
WARN('KefinTweaksUtils.onViewPage not available');
}
- function addCustomWatchlistPage() {
+ async function addCustomWatchlistPage() {
if (!window.KefinTweaksUtils?.addCustomPage) {
WARN('addCustomPage not available yet');
return false;
}
+ const watchlistUrl = await window.KefinTweaksUtils.getWatchlistUrl();
window.KefinTweaksUtils.addCustomPage(
- '#/watchlist',
+ watchlistUrl,
'Watchlist',
''
);
@@ -7433,21 +7433,32 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
return;
}
+ const watchlistCfg = window.KefinTweaksFeatureConfigs?.watchlist?.normalizeConfig?.(
+ window.KefinTweaksConfig?.watchlist
+ ) || {
+ sideMenu: window.KefinTweaksConfig?.watchlist?.sideMenu !== false,
+ topNavigation: window.KefinTweaksConfig?.watchlist?.topNavigation !== false
+ && window.KefinTweaksConfig?.watchlist?.topNavigation !== 'none',
+ userMenu: window.KefinTweaksConfig?.watchlist?.userMenu !== false
+ };
+
const watchlistTabIndex = await getWatchlistTabIndex();
const options = {
- userMenu: true,
+ sideMenu: watchlistCfg.sideMenu !== false,
+ userMenu: watchlistCfg.userMenu === true,
order: 2
};
let watchlistUrl = '#/watchlist';
- // Major version of Jellyfin
- const jellyfinVersion = await window.KefinTweaks.getJellyfinMajorVersion();
- const isModernUI = document.querySelector('.MuiBox-root') !== null || localStorage.getItem('layout')?.length === 0;
+ const isModernUI = window.KefinTweaksUtils.isModernUI();
+ const wantTopNav = watchlistCfg.topNavigation !== false && watchlistCfg.topNavigation !== 'none';
// If watchlist tab index is null or undefined, add to top navigation
- if (watchlistTabIndex === null || watchlistTabIndex === undefined || isModernUI) {
+ if (wantTopNav && (watchlistTabIndex === null || watchlistTabIndex === undefined || isModernUI)) {
options.topNavigation = 'main';
+ } else if (!wantTopNav) {
+ options.topNavigation = 'none';
} else {
watchlistUrl = `#/home?tab=${watchlistTabIndex}`;
}