Added Wikipedia's own Dark Mode solution (#1320)

* Addede wikipedia's dark mode

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Fixed codefactor issue

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Removed overriding classes

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Fixed dark mode conversion

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* fixed another little update

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Added wikipedia's dark theme for zim

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* fixed codefactor

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* adding css file in sw file

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Added feature to switch to a standard inversion dark mode if the user loads a non-Wikimedia ZIM

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* added explicit check for the theme

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Fixed dropdown issue and add asterisk

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* fixing white flash

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* fixing eslint

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Fixing codeerror

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* REVERTING

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Revert "REVERTING"

This reverts commit c515392dcf1acdafdd8023989ddeb8bb0da60c74.

* Fixed color

* Removing unncessary code which was added to fix flash issue

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Add translations in i18n dir

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Added in description

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>

* Update i18n/es.jsonp.js

Co-authored-by: Jaifroid <egk10@cam.ac.uk>

* Update i18n/fr.jsonp.js

Co-authored-by: Jaifroid <egk10@cam.ac.uk>

* Update i18n/fr.jsonp.js

Co-authored-by: Jaifroid <egk10@cam.ac.uk>

---------

Signed-off-by: THEBOSS0369 <anujkumsharma9876@gmail.com>
Co-authored-by: Jaifroid <egk10@cam.ac.uk>
This commit is contained in:
Anuj Kumar Sharma 2025-06-14 22:10:19 +05:30 committed by GitHub
parent ec52b0a6b7
commit 0e531b6e3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1331 additions and 4 deletions

View File

@ -57,11 +57,13 @@ document.localeJson = {
"configure-display-themeoption-dark-apponly": "Dark (app only)",
"configure-display-themeoption-dark-invert": "Dark (generic invert)",
"configure-display-themeoption-dark-mwinvert": "Dark (wikimedia invert)",
"configure-display-themeoption-dark-wikivector": "Dark (wikipedia vector)",
"configure-display-themeoption-auto-apponly": "Auto (app only)",
"configure-display-themeoption-auto-invert": "Auto (generic invert)",
"configure-display-themeoption-auto-mwinvert": "Auto (wikimedia invert)",
"configure-display-apptheme-auto-description": "&emsp;(Auto themes match the dark/light mode of your device.)",
"configure-display-apptheme-mwinvert-description": "* Implements workarounds specific to Wikimedia ZIMs. Try generic option if there are display errors with recent ZIMs.",
"configure-display-apptheme-wikivector-description": "* Only applies to Wikimedia ZIMs. Falls back to generic inversion when other archives are loaded.",
"configure-display-apptheme-info": "[&nbsp;Show article with applied theme&nbsp;]",
"configure-display-libzimsearchtype": "<b>Add snippets to libzim fulltext search</b> (if available)",
"configure-display-libzimsearchtype-tip": "If checked, uses libzim fulltext search with snippet previews if available, otherwise just gets fulltext titles. Hover, click or press enter on keyboard to expand the snippet.",

View File

@ -57,11 +57,13 @@ document.localeJson = {
"configure-display-themeoption-dark-apponly": "Oscuro (sólo aplicación)",
"configure-display-themeoption-dark-invert": "Oscuro (inversión genérica)",
"configure-display-themeoption-dark-mwinvert": "Oscuro (inversión wikimedia)",
"configure-display-themeoption-dark-wikivector": "Oscuro (estilo vector de wikipedia)",
"configure-display-themeoption-auto-apponly": "Auto (sólo aplicación)",
"configure-display-themeoption-auto-invert": "Auto (inversión genérica)",
"configure-display-themeoption-auto-mwinvert": "Auto (inversión wikimedia)",
"configure-display-apptheme-auto-description": "&emsp;(Temas automáticos se adaptan al modo oscuro/claro de su dispositivo.)",
"configure-display-apptheme-mwinvert-description": "* Implementa soluciones específicas para ZIMs de Wikimedia. Pruebe la opción genérica si hay errores de visualización con otros ZIMs.",
"configure-display-apptheme-wikivector-description": "* Solo se aplica a los ZIM de Wikimedia. Se revierte a la inversión genérica cuando se cargan otros archivos.",
"configure-display-apptheme-info": "[&nbsp;Mostrar artículo con tema seleccionado&nbsp;]",
"configure-display-libzimsearchtype": "<b>Agregar fragmentos a la búsqueda de texto completo libzim</b> (si están disponibles)",
"configure-display-libzimsearchtype-tip": "Si está marcado, usa la búsqueda de texto completo libzim con vistas previas de fragmentos si están disponibles, de lo contrario solo obtiene títulos de texto completo. Pase el cursor, haga clic o presione enter en el teclado para expandir el fragmento.",

View File

@ -57,11 +57,13 @@ document.localeJson = {
"configure-display-themeoption-dark-apponly": "Sombre (application uniquement)",
"configure-display-themeoption-dark-invert": "Sombre (inversion générique)",
"configure-display-themeoption-dark-mwinvert": "Sombre (inversion Wikimédia)",
"configure-display-themeoption-dark-wikivector": "Sombre (style vector de Wikipédia)",
"configure-display-themeoption-auto-apponly": "Automatique (application uniquement)",
"configure-display-themeoption-auto-invert": "Automatique (inversion générique)",
"configure-display-themeoption-auto-mwinvert": "Automatique (inversion Wikimédia)",
"configure-display-apptheme-auto-description": "&emsp;(Les thèmes automatiques s'adaptent au mode sombre/clair de votre appareil.)",
"configure-display-apptheme-mwinvert-description": "* Implémente des solutions spécifiques aux ZIM de Wikimédia. Essayez l'option générique si vous rencontrez des problèmes d'affichage avec d'autres ZIM.",
"configure-display-apptheme-wikivector-description": "* Sapplique uniquement aux ZIMs de Wikimédia. Revient à linversion générique lorsque dautres archives sont chargées.",
"configure-display-apptheme-info": "[&nbsp;Afficher l'article avec le thème sélectionné&nbsp;]",
"configure-display-libzimsearchtype": "<b>Ajouter des extraits à la recherche en texte intégral libzim</b> (si disponible)",
"configure-display-libzimsearchtype-tip": "Si coché, utilise la recherche en texte intégral libzim avec des aperçus d'extraits si disponibles, sinon obtient seulement les titres en texte intégral. Survolez, cliquez ou appuyez sur Entrée au clavier pour développer l'extrait.",

View File

@ -116,6 +116,7 @@ const precacheFiles = [
'www/css/app.css',
'www/css/kiwixJS_invert.css',
'www/css/kiwixJS_mwInvert.css',
'www/css/kiwixJS_wikiVector.css',
'www/css/transition.css',
'www/img/icons/kiwix-256.png',
'www/img/icons/kiwix-32.png',

View File

@ -550,3 +550,4 @@ button {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -567,12 +567,14 @@
<option data-i18n="configure-display-themeoption-dark-apponly" value="dark">Dark (app only)</option>
<option data-i18n="configure-display-themeoption-dark-invert" value="dark_invert">Dark (generic invert)</option>
<option data-i18n="configure-display-themeoption-dark-mwinvert" value="dark_mwInvert">Dark (wikimedia invert)*</option>
<option data-i18n="configure-display-themeoption-dark-wikivector" id="theme-vector-option" title="Vector style only available for Wikimedia ZIMs" value="dark_wikiVector">Dark (Wikipedia Vector)*</option>
<option data-i18n="configure-display-themeoption-auto-apponly" class="auto" value="auto">Auto (app only)</option>
<option data-i18n="configure-display-themeoption-auto-invert" class="auto" value="auto_invert">Auto (generic invert)</option>
<option data-i18n="configure-display-themeoption-auto-mwinvert" class="auto" value="auto_mwInvert">Auto (wikimedia invert)*</option>
</select>
<p data-i18n="configure-display-apptheme-auto-description" class="text-muted" id="kiwix-auto-description" style="margin-bottom: 0;">&emsp;(Auto themes match the dark/light mode of your device.)</p>
<p data-i18n="configure-display-apptheme-mwinvert-description" id="mwInvert-help" class="appThemeInfo form-text text-muted">* Implements workarounds specific to Wikimedia ZIMs. Try generic option if there are display errors with recent ZIMs.</p>
<p data-i18n="configure-display-apptheme-wikivector-description" id="wikiVector-help" class="appThemeInfo form-text text-muted">* Only applies to Wikimedia ZIMs. Falls back to generic inversion when other archives are loaded.</p>
<a data-i18n="configure-display-apptheme-info" id="viewArticle" class="appThemeInfo form-text" href="#">[&nbsp;Show article with applied theme&nbsp;]</a>
</div>
<div class="checkbox" id="libzimSearchTypeCheckBox">

View File

@ -126,6 +126,50 @@ darkPreference.onchange = function () {
uiUtil.applyAppTheme(params.appTheme);
}
// Vector Dark theme update the Dropdown UI State
function updateThemeOptions() {
const vectorOption = document.getElementById('theme-vector-option');
if (vectorOption) {
// Only disable if a ZIM is loaded and it's not a Wikimedia ZIM
const zimLoaded = selectedArchive && selectedArchive.file && selectedArchive.file.name;
vectorOption.disabled = zimLoaded && !params.isWikimediaZim;
vectorOption.title = (!zimLoaded || params.isWikimediaZim) ? "" : "Vector style only available for Wikimedia ZIMs";
// Check that dropdown matches actual theme
const currentTheme = document.getElementById('appThemeSelect')?.value;
if (currentTheme && currentTheme.includes('_wikiVector') && !params.isWikimediaZim) {
// If somehow Vector is selected for non Wikimedia ZIM then correct it
handleThemeFallback();
}
}
}
function handleThemeFallback() {
const themeSelect = document.getElementById('appThemeSelect');
if (!themeSelect) return;
const currentTheme = themeSelect.value || settingsStore.getItem('appTheme') || 'light';
// When switching to Wikimedia ZIM
if (params.isWikimediaZim) {
// If current theme is invert then try to restore Vector if it was previously used
if (currentTheme.includes('_invert')) {
const baseTheme = currentTheme.replace('_invert', '_wikiVector');
if (themeSelect.querySelector(`option[value="${baseTheme}"]`)) {
themeSelect.value = baseTheme;
uiUtil.applyAppTheme(baseTheme);
}
}
}
// When switching from Wikimedia ZIM
else if (currentTheme.includes('_wikiVector')) {
const newTheme = currentTheme.replace('_wikiVector', '_invert');
themeSelect.value = newTheme;
uiUtil.applyAppTheme(newTheme);
}
updateThemeOptions();
}
/**
* Resize the IFrame height, so that it fills the whole available height in the window
*/
@ -1868,6 +1912,9 @@ async function archiveReadyCallback (archive) {
}
// This flag will be reset each time a new archive is loaded
appstate.wikimediaZimLoaded = /wikipedia|wikivoyage|mdwiki|wiktionary/i.test(archive.file.name);
params.isWikimediaZim = /wikipedia|wikimedia|wikivoyage|wiktionary|wikibooks|wikiquote|wikisource|wikinews|wikiversity/i.test(archive.file.name);
updateThemeOptions();
handleThemeFallback();
// Set contentInjectionMode to serviceWorker when opening a new archive in case the user switched to Restricted Mode/jquery Mode when opening the previous archive
if (params.contentInjectionMode === 'jquery') {
params.contentInjectionMode = settingsStore.getItem('contentInjectionMode');
@ -2306,6 +2353,7 @@ function articleLoadedSW (iframeArticleContent) {
// Display the iframe content
iframeArticleContent.style.display = '';
articleContainer.style.display = '';
// Deflect drag-and-drop of ZIM file on the iframe to Config
if (!params.disableDragAndDrop) {
var doc = iframeArticleContent.contentDocument ? iframeArticleContent.contentDocument.documentElement : null;
@ -2720,6 +2768,7 @@ function displayArticleContentInIframe (dirEntry, htmlArticle) {
if (!isDirEntryExpectedToBeDisplayed(dirEntry)) {
return;
}
// Display Bootstrap warning alert if the landing page contains active content
if (!params.hideActiveContentWarning && params.isLandingPage) {
if (regexpActiveContent.test(htmlArticle) || /zimit/.test(selectedArchive.zimType)) {

View File

@ -853,10 +853,21 @@ function tabTransitionToSection (toSection, isAnimationRequired = false) {
* @param {String} theme The theme to apply (light|dark[_invert|_mwInvert]|auto[_invert|_mwInvert])
*/
function applyAppTheme (theme) {
// Validate the theme parameter to prevent XSS
// Only allow specific valid theme formats
if (!theme.match(/^(light|dark|auto)(_invert|_mwInvert|_wikiVector)?$/)) {
console.error('Invalid theme format:', theme);
theme = 'light';
}
// Resolve the app theme from the matchMedia preference (for auto themes) or from the theme string
var appTheme = isDarkTheme(theme) ? 'dark' : 'light';
// Get contentTheme from chosen theme
var contentTheme = theme.replace(/^[^_]*/, '');
// Revert to '_invert' or default dark theme if trying to use '_wikiVector' on non-Wikimedia ZIMs
if (contentTheme === '_wikiVector' && !params.isWikimediaZim) {
contentTheme = '_invert';
}
var htmlEl = document.querySelector('html');
var footer = document.querySelector('footer');
var oldTheme = htmlEl.dataset.theme || '';
@ -877,19 +888,28 @@ function applyAppTheme (theme) {
footer.classList.add(contentTheme || '_light');
// Embed a reference to applied theme, so we can remove it generically in the future
htmlEl.dataset.theme = appTheme + contentTheme;
// Safely handle help element IDs
var safeOldContentTheme = oldContentTheme.replace(/[^a-zA-Z0-9-]/g, '');
var safeContentTheme = contentTheme.replace(/[^a-zA-Z0-9-]/g, '');
// Hide any previously displayed help
var oldHelp = document.getElementById(oldContentTheme.replace(/_/, '') + '-help');
var oldHelp = document.getElementById(safeOldContentTheme.replace(/_/, '') + '-help');
if (oldHelp) oldHelp.style.display = 'none';
// Show any specific help for selected contentTheme
var help = document.getElementById(contentTheme.replace(/_/, '') + '-help');
var help = document.getElementById(safeContentTheme.replace(/_/, '') + '-help');
if (help) help.style.display = 'block';
// Remove the contentTheme for auto themes whenever system is in light mode
if (/^auto/.test(theme) && appTheme === 'light') contentTheme = null;
// Hide any previously displayed description for auto themes
var oldDescription = document.getElementById('kiwix-auto-description');
if (oldDescription) oldDescription.style.display = 'none';
// Safely handle description element IDs
var safeThemeBase = theme.replace(/_.*$/, '').replace(/[^a-zA-Z0-9-]/g, '');
// Show description for auto themes
var description = document.getElementById('kiwix-' + theme.replace(/_.*$/, '') + '-description');
var description = document.getElementById('kiwix-' + safeThemeBase + '-description');
if (description) description.style.display = 'block';
// If there is no ContentTheme or we are applying a different ContentTheme, remove any previously applied ContentTheme
if (oldContentTheme && oldContentTheme !== contentTheme) {
@ -913,7 +933,8 @@ function applyAppTheme (theme) {
link.setAttribute('id', 'kiwixJSTheme');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('href', prefix + '/css/kiwixJS' + contentTheme + '.css');
var safeContentThemeForURL = contentTheme.replace(/[^a-zA-Z0-9_-]/g, '');
link.setAttribute('href', prefix + '/css/kiwixJS' + safeContentThemeForURL + '.css');
doc.head.appendChild(link);
}
}