From 053780ee83cad22da59a45f2110d174e569d8439 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 09:28:59 +0000 Subject: [PATCH 01/17] Migrate from Bootstrap 3 to Bootstrap 4.6.2 and replace Glyphicons with Font Awesome 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major changes: - Upgraded Bootstrap 3.3.7 to Bootstrap 4.6.2 for modern CSS framework - Replaced Glyphicons with Font Awesome 5.15.4 for better Tailwind compatibility - Removed jQuery dependency (was unused, Bootstrap JS was already disabled) - Updated all HTML components: panels → cards, glyphicons → Font Awesome icons - Migrated app.css overrides to use Bootstrap 4 class names - Updated rollup.config.js to copy Bootstrap 4 and Font Awesome assets to dist Technical details: - .panel/.panel-heading/.panel-body → .card/.card-header/.card-body - .panel-info/warning/danger → .card.border-info/warning/danger - All 15 unique Glyphicons replaced with Font Awesome equivalents - Updated documentation in About section to reflect new libraries - Maintained IE11 and Edge 18 compatibility (Bootstrap 4.6.2 supports IE10+) This migration prepares the codebase for future Tailwind CSS adoption by: 1. Using framework-agnostic Font Awesome icons 2. Modernizing component structure (cards vs panels) 3. Eliminating jQuery dependency --- package-lock.json | 79 +++++++++++++++++++++-- package.json | 5 +- rollup.config.js | 19 +++--- www/css/app.css | 34 +++++----- www/index.html | 159 +++++++++++++++++++++++----------------------- 5 files changed, 183 insertions(+), 113 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71adc663e..dd3572b5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,19 +14,20 @@ "electron-context-menu": "^3.1.1", "electron-store": "^8.1.0", "electron-updater": "^6.6.8", - "express": "^4.21.0", - "jquery": "^3.7.1" + "express": "^4.21.0" }, "devDependencies": { "@babel/cli": "^7.21.5", "@babel/core": "^7.21.5", "@babel/preset-env": "^7.21.5", + "@fortawesome/fontawesome-free": "^5.15.4", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", "babel-plugin-polyfill-corejs3": "^0.7.1", + "bootstrap": "^4.6.2", "del-cli": "^5.0.0", "electron": "38.3.0", "electron-builder": "^26.0.18", @@ -2698,6 +2699,17 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", + "dev": true, + "hasInstallScript": true, + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "engines": { + "node": ">=6" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -4304,6 +4316,28 @@ "dev": true, "optional": true }, + "node_modules/bootstrap": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", + "deprecated": "This version of Bootstrap is no longer supported. Please upgrade to the latest version.", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "jquery": "1.9.1 - 3", + "popper.js": "^1.16.1" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -8089,7 +8123,9 @@ "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "dev": true, + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -9371,6 +9407,19 @@ "node": ">=10.4.0" } }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -13146,6 +13195,12 @@ "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", "dev": true }, + "@fortawesome/fontawesome-free": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", + "dev": true + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -14245,6 +14300,13 @@ "dev": true, "optional": true }, + "bootstrap": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", + "dev": true, + "requires": {} + }, "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -16923,7 +16985,9 @@ "jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "dev": true, + "peer": true }, "js-tokens": { "version": "4.0.0", @@ -17827,6 +17891,13 @@ "xmlbuilder": "^15.1.1" } }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "dev": true, + "peer": true + }, "postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", diff --git a/package.json b/package.json index b0f547593..9362bc0a5 100644 --- a/package.json +++ b/package.json @@ -182,12 +182,14 @@ "@babel/cli": "^7.21.5", "@babel/core": "^7.21.5", "@babel/preset-env": "^7.21.5", + "@fortawesome/fontawesome-free": "^5.15.4", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", "babel-plugin-polyfill-corejs3": "^0.7.1", + "bootstrap": "^4.6.2", "del-cli": "^5.0.0", "electron": "38.3.0", "electron-builder": "^26.0.18", @@ -206,7 +208,6 @@ "electron-context-menu": "^3.1.1", "electron-store": "^8.1.0", "electron-updater": "^6.6.8", - "express": "^4.21.0", - "jquery": "^3.7.1" + "express": "^4.21.0" } } diff --git a/rollup.config.js b/rollup.config.js index 2c027e768..ccc4d8457 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -43,9 +43,12 @@ const config = { copy({ targets: [{ src: ['www/js/lib/*dec-wasm.wasm', 'www/js/lib/libzim-asm.js', 'www/js/lib/libzim-wasm.*', 'www/js/lib/darkreader.min.js', 'www/js/lib/webpHeroBundle*', - 'node_modules/jquery/dist/jquery.slim.min.*', '!www/js/lib/libzim-wasm.dev*'], + '!www/js/lib/libzim-wasm.dev*'], dest: 'dist/www/js' }, + { src: ['node_modules/bootstrap/dist/css/bootstrap.min.css'], dest: 'dist/www/css' }, + { src: ['node_modules/@fortawesome/fontawesome-free/css/all.min.css'], dest: 'dist/www/css' }, + { src: ['node_modules/@fortawesome/fontawesome-free/webfonts/*'], dest: 'dist/www/webfonts' }, { src: ['archives', 'images', 'index.html', 'manifest.json', 'package.json', 'LICENSE', 'CHANGELOG.md', 'README.md', '*.appxmanifest', '*.pfx', '*.cjs', 'Package.StoreAssociation.xml'], dest: 'dist' } ], flatten: true @@ -147,10 +150,9 @@ if (process.env.BUILD === 'production') { .replace(/bundle\.js/, 'bundle.min.js') // Comment out the old app.js link .replace(/( - + From 5d41fa08bbc335f382141b630acd0c6a0f6efb82 Mon Sep 17 00:00:00 2001 From: Jaifroid Date: Tue, 18 Nov 2025 13:59:13 +0000 Subject: [PATCH 02/17] Patch gitignore correctly --- .gitignore | 1 + scripts/patch_gitignore.sh | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index f5d1935f3..bcb99037c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ _pkginfo.txt /bin/ /BundleArtifacts/ /dist/ +/dist/node_modules /archives/*.zim* /archives/*wikivoyage*.zim /archives/*medicine*.zim diff --git a/scripts/patch_gitignore.sh b/scripts/patch_gitignore.sh index 846db1e19..1417f50f6 100644 --- a/scripts/patch_gitignore.sh +++ b/scripts/patch_gitignore.sh @@ -3,10 +3,13 @@ # Delete gitignore entry for the dist folder sed -i "/^\/dist\/$/d" .gitignore -# Replace the /node_modules*/ entry in gitignore with the following -sed -i 's|/node_modules\*/|/node_modules/*\ -!/node_modules/jquery\ -/node_modules/jquery/*\ -!/node_modules/jquery/dist\ -/node_modules/jquery/dist/*\ -!/node_modules/jquery/dist/jquery.slim.min.*|' .gitignore +# Replace the /*node_modules*/ entry in gitignore with the following +sed -i 's|/\*node_modules\*/|/node_modules/*\ +!/node_modules/bootstrap\ +/node_modules/bootstrap/*\ +!/node_modules/bootstrap/dist\ +/node_modules/bootstrap/dist/*\ +!/node_modules/bootstrap/dist/css\ +!/node_modules/bootstrap/dist/css/*\ +!/node_modules/bootstrap/dist/js\ +!/node_modules/bootstrap/dist/js/*|' .gitignore From 6af7f28a5d424be88e9d3cb817f32e2a3fc11baf Mon Sep 17 00:00:00 2001 From: Jaifroid Date: Tue, 18 Nov 2025 14:02:55 +0000 Subject: [PATCH 03/17] Update package-lock.json --- package-lock.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd3572b5d..86d684fcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8135,9 +8135,10 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -16996,9 +16997,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "requires": { "argparse": "^2.0.1" } From e601e0e55428cb20bbaf1cb0bc0d3f485cbb2cb6 Mon Sep 17 00:00:00 2001 From: Jaifroid Date: Tue, 18 Nov 2025 16:37:41 +0000 Subject: [PATCH 04/17] Better styles --- www/css/app.css | 114 ++++++++++++++++++++++++++++++++++++++++++++---- www/index.html | 110 +++++++++++++++++++++++----------------------- 2 files changed, 159 insertions(+), 65 deletions(-) diff --git a/www/css/app.css b/www/css/app.css index cb64fc4fb..c4b6685f4 100644 --- a/www/css/app.css +++ b/www/css/app.css @@ -243,10 +243,11 @@ div:not(.card.border-success, .alert-message) { padding: 5px 1em !important; } -.navbar-inverse { +.navbar-inverse, .navbar-dark { background-color: #d9edf7 !important; border-color: #d9edf7 !important; margin-bottom: 0 !important; + padding: 0 !important; } .dark .form-control { @@ -264,7 +265,33 @@ div:not(.card.border-success, .alert-message) { .category-padding { margin: 0 auto; - max-width: 800px; + padding: 0 15px; +} + +/* Override Bootstrap 4 container max-width to use full width */ +.container { + max-width: 100%; +} + +/* Bootstrap 4 card heading styles */ +.card-heading { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0,0,0,.03); + border-bottom: 1px solid rgba(0,0,0,.125); + font-weight: bold; +} + +.dark .card-heading { + background-color: rgba(255,255,255,.03); + border-bottom: 1px solid rgba(255,255,255,.125); +} + +.card-group-heading { + font-size: 1.5rem; + font-weight: 500; + margin-top: 1rem; + margin-bottom: 0.5rem; } .apiAvailable { @@ -345,6 +372,7 @@ div:not(.card.border-success, .alert-message) { .navbar-header { float: none !important; + width: 100%; -webkit-app-region: no-drag; } @@ -360,11 +388,68 @@ div:not(.card.border-success, .alert-message) { -webkit-app-region: drag; } -nav a.btn-primary, .input-group-addon { +nav a.btn-primary, .input-group-text { background: #d9edf7; border-color: #d9edf7 !important; - margin-top: 3px; - padding: 5px 1em !important; + color: #000 !important; +} + +.dark nav a.btn-primary { + color: #fff !important; +} + +/* Bootstrap 4 input-group fixes */ +#navbar .input-group { + display: flex; + flex-wrap: nowrap; + align-items: center; + margin: 3px 0 0 0; + padding: 0; + width: 100%; +} + +#navbar .input-group .btn { + border-radius: 0; + margin: 0; + padding: 5px 1em; + height: auto; + line-height: 1.5; + flex-shrink: 0; +} + +#navbar .input-group .form-control { + margin: 0; + padding: 5px 1em; + height: auto !important; + flex-grow: 1; + flex-shrink: 1; +} + +#navbar .input-group .input-group-text { + margin: 0; + padding: 5px 1em; + height: auto; + display: flex; + align-items: center; + flex-shrink: 0; +} + +#navbar .container-fluid { + padding: 0; + width: 100%; +} + +#navbar form { + width: 100%; + margin: 0; + padding: 0; +} + +#navbar #row, +#navbar #row2 { + margin: 0; + padding: 0; + width: 100%; } .btn:active, .btn-primary:active, .btn-primary.active { @@ -406,7 +491,7 @@ nav a.btn-primary, .input-group-addon { } } -.dark .btn-primary, .dark .btn-secondary, .dark .navbar-inverse { +.dark .btn-primary, .dark .btn-secondary, .dark .navbar-inverse, .dark .navbar-dark { background: #222 !important; border-color: #222 !important; } @@ -600,7 +685,7 @@ pre { background-color: darkslategray !important; } -.dark .btn-default, .dark .input-group-addon, .dark pre { +.dark .btn-default, .dark .input-group-text, .dark pre { background: #222 !important; border-color: #222 !important; color: lightgray !important; @@ -659,6 +744,17 @@ pre { /*********************************/ /* Hide the browser's default checkbox */ +/* Custom checkbox/radio container */ +label.checkbox, +label.radio { + position: relative; + padding-left: 30px; + display: block; + cursor: pointer; + -webkit-user-select: none; + user-select: none; +} + .container input[type=checkbox], .container input[type=radio] { position: absolute; opacity: 0; @@ -671,7 +767,7 @@ pre { .checkmark { position: absolute; top: 0; - left: -25px; + left: 0; height: 20px; width: 20px; background-color: whitesmoke; @@ -705,7 +801,7 @@ pre { .radiobtn { position: absolute; top: 0; - left: -25px; + left: 0; height: 20px; width: 20px; background-color: whitesmoke; diff --git a/www/index.html b/www/index.html index b7b55f663..f76866f98 100644 --- a/www/index.html +++ b/www/index.html @@ -247,38 +247,34 @@

-