From e2409268fd6f7aea21dc1f9c5efd904bbe6fca90 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Tue, 9 Jun 2026 19:05:13 +0530 Subject: [PATCH 1/3] fix: ad references, terminal HTTP safety, banner sync, and defense fixes - Replace window.ad/window.iad with module-level bannerAd/interstitialAd imports from startAd.js across adRewards, helpers, keyboard, remoteStorage, plugin, and extensions modules - Export ad unit IDs, bannerAd, interstitialAd, and initialized from startAd for consistent module-level references - Guard terminal createPid/resize/terminate against missing cordova HTTP plugin with null-safe sendRequest lookup and improved error fallback - Restore admob?.RewardedAd guard in createRewardedAd and adUnitIdRewarded check in isRewardedSupported - Fix banner ad toggle to fire on every resume event, not just on height change - Skip system.clearCache on fresh install by adding previousVersionCode != null check - Escape regex special chars in font name before building @font-face regex - Add error logging to previously silent catch block in devTools eruda download - Restore rating-medium CSS class for 50-79% plugin ratings in plugin.view.js - Fix ES6 detection in index.html by invoking the returned arrow function - Add trailing newline to index.html Or a shorter version: fix: tighten ad references, terminal safety, and defensive fixes Replace global window.ad/window.iad with module-level exports from startAd.js. Add cordova HTTP plugin availability guards in terminal (create/resize/terminate). Restore missing safety checks in adRewards (RewardedAd guard, unit ID check). Fix banner ad not syncing on resume without keyboard height change, skip clearCache on fresh install, escape font name in regex, and fix silent catch in devTools eruda download. --- config.xml | 1 + package-lock.json | 16 + package.json | 6 +- rspack.config.js | 10 - src/components/audioPlayer/style.scss | 5 + src/components/lspInfoDialog/styles.scss | 16 + src/components/lspStatusBar/style.scss | 8 + src/components/referencesPanel/styles.scss | 15 + src/components/settingsPage.scss | 288 ++++++++------- src/components/sideButton/style.scss | 4 + src/components/sidebar/style.scss | 9 + src/components/symbolsPanel/styles.scss | 10 + src/components/terminal/terminal.js | 64 ++-- src/components/terminal/terminalManager.js | 38 +- src/handlers/keyboard.js | 16 +- src/lib/adRewards.js | 21 +- src/lib/devTools.js | 3 +- src/lib/fonts.js | 3 +- src/lib/polyfill.js | 30 ++ src/lib/remoteStorage.js | 5 +- src/lib/startAd.js | 123 ++++--- src/main.js | 1 + src/main.scss | 7 + src/pages/about/about.scss | 5 + src/pages/adRewards/adRewards.scss | 8 + src/pages/changelog/style.scss | 6 +- src/pages/fileBrowser/fileBrowser.scss | 4 + src/pages/fontManager/style.scss | 10 +- src/pages/markdownPreview/style.scss | 4 + src/pages/plugin/plugin.js | 6 +- src/pages/plugin/plugin.scss | 22 ++ src/pages/plugin/plugin.view.js | 2 +- src/pages/plugins/plugins.scss | 9 + src/pages/quickTools/style.scss | 4 + src/pages/sponsor/style.scss | 4 + src/pages/sponsors/style.scss | 4 + src/pages/themeSetting/themeSetting.scss | 14 + src/pages/welcome/welcome.scss | 8 + .../admob/src/android/cordova/ads/Banner.kt | 97 ++--- src/plugins/admob/src/android/core/util.kt | 8 +- src/plugins/system/www/plugin.js | 2 +- src/sidebarApps/extensions/index.js | 5 +- src/sidebarApps/extensions/style.scss | 33 +- src/sidebarApps/notification/style.scss | 7 + src/styles/codemirror.scss | 7 + src/styles/fileInfo.scss | 6 + src/styles/wideScreen.scss | 9 + src/utils/helpers.js | 28 +- www/index.html | 340 +++++++++--------- 49 files changed, 831 insertions(+), 520 deletions(-) diff --git a/config.xml b/config.xml index 236eea5e7..8c3ef4d9d 100644 --- a/config.xml +++ b/config.xml @@ -38,6 +38,7 @@ + { syntax: 'typescript', tsx: false, }, - transform: { - // react: { - // pragma: 'tag', - // pragmaFrag: 'Array', - // throwIfNamespace: false, - // development: false, - // useBuiltins: false, - // runtime: 'classic', - // }, - }, target: 'es2015', }, }, diff --git a/src/components/audioPlayer/style.scss b/src/components/audioPlayer/style.scss index 18f71d72c..c28a4d275 100644 --- a/src/components/audioPlayer/style.scss +++ b/src/components/audioPlayer/style.scss @@ -1,3 +1,8 @@ +@supports not (gap: 1px) { + .audio-player > * + * { margin-left: 15px; } + .volume-control > * + * { margin-left: 8px; } +} + .audio-player { background: var(--primary-color, #1e1e1e); border-radius: 10px; diff --git a/src/components/lspInfoDialog/styles.scss b/src/components/lspInfoDialog/styles.scss index be11bbfef..f2415240a 100644 --- a/src/components/lspInfoDialog/styles.scss +++ b/src/components/lspInfoDialog/styles.scss @@ -454,4 +454,20 @@ font-weight: 600; color: var(--popup-text-color); font-family: monospace; +} + +@supports not (gap: 1px) { + .lsp-list-actions > * + * { margin-left: 8px; } + .lsp-action-btn > * + * { margin-left: 6px; } + .lsp-server-item > * + * { margin-left: 12px; } + .lsp-server-info > * + * { margin-top: 2px; } + .lsp-details-header > * + * { margin-left: 8px; } + .lsp-header-actions > * + * { margin-left: 4px; } + .lsp-details-title > * + * { margin-left: 10px; } + .lsp-chip-container > * { margin-right: 5px; margin-bottom: 5px; } + .lsp-logs-actions > * + * { margin-left: 2px; } + .lsp-log > * + * { margin-left: 6px; } + .lsp-logs-title > * + * { margin-left: 4px; } + .lsp-stats-container > * + * { margin-left: 16px; } + .lsp-stat > * + * { margin-left: 6px; } } \ No newline at end of file diff --git a/src/components/lspStatusBar/style.scss b/src/components/lspStatusBar/style.scss index ac0a78e63..0872b3fba 100644 --- a/src/components/lspStatusBar/style.scss +++ b/src/components/lspStatusBar/style.scss @@ -163,6 +163,14 @@ wc-page:not([footer-height]) #lsp-status-bar { bottom: 10px; } +@supports not (gap: 1px) { + #lsp-status-bar { + .lsp-status-content > * + * { margin-left: 10px; } + .lsp-status-text > * + * { margin-top: 2px; } + .lsp-status-progress > * + * { margin-left: 8px; } + } +} + @keyframes lspStatusSlideUp { from { transform: translateY(100%); diff --git a/src/components/referencesPanel/styles.scss b/src/components/referencesPanel/styles.scss index b2c8a124b..b73fa2e0a 100644 --- a/src/components/referencesPanel/styles.scss +++ b/src/components/referencesPanel/styles.scss @@ -298,6 +298,21 @@ } } +@supports not (gap: 1px) { + .references-panel { + .header-content > * + * { margin-top: 2px; } + .header-title > * + * { margin-left: 8px; } + .header-actions > * + * { margin-left: 4px; } + .loading-state > * + * { margin-left: 12px; } + } + .ref-file-header > * + * { margin-left: 8px; } + .ref-item > * + * { margin-left: 10px; } + .references-tab-container { + .header-info > * + * { margin-left: 10px; } + .loading-state > * + * { margin-left: 12px; } + } +} + .references-tab-container { display: flex; flex-direction: column; diff --git a/src/components/settingsPage.scss b/src/components/settingsPage.scss index aba4f617f..4990981d1 100644 --- a/src/components/settingsPage.scss +++ b/src/components/settingsPage.scss @@ -2,19 +2,20 @@ #{$list-selector} { display: flex; flex-direction: column; - gap: 1.2rem; width: 100%; max-width: 48rem; margin: 0 auto; padding: 0.5rem 0 5.5rem; box-sizing: border-box; background: var(--secondary-color); + + > *:not(:first-child) { + margin-top: 1.2rem; + } } .settings-section { - display: flex; - flex-direction: column; - gap: 0; + display: block; width: 100%; } @@ -37,13 +38,13 @@ .settings-section-card { width: 100%; - overflow: hidden; box-sizing: border-box; } } @mixin settings-icon-reset { .icon { + &:active, &.active { transform: none; @@ -56,8 +57,8 @@ wc-page.main-settings-page { background: var(--secondary-color); @include settings-page-shell(".main-settings-list"); - .main-settings-list > .list-item, - .settings-section-card > .list-item { + .main-settings-list>.list-item, + .settings-section-card>.list-item { display: flex; width: 100%; min-height: 64px; @@ -65,12 +66,17 @@ wc-page.main-settings-page { padding: 0.75rem 1rem; box-sizing: border-box; align-items: center; - gap: 0.85rem; background: transparent; cursor: pointer; + > *:not(:first-child) { + margin-left: 0.85rem; + } + &.no-leading-icon { - gap: 0; + > *:not(:first-child) { + margin-left: 0; + } } &:not(:last-of-type) { @@ -84,14 +90,14 @@ wc-page.main-settings-page { background: color-mix(in srgb, var(--secondary-color), var(--popup-text-color) 4%); } - > .icon.no-icon { + >.icon.no-icon { width: 0; min-width: 0; height: 0; margin: 0; } - > .icon:first-child:not(.no-icon) { + >.icon:first-child:not(.no-icon) { display: flex; align-items: center; justify-content: center; @@ -103,15 +109,18 @@ wc-page.main-settings-page { color: color-mix(in srgb, var(--secondary-text-color), transparent 18%); } - > .container { + >.container { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: visible; - gap: 0.2rem; - > .text { + > *:not(:first-child) { + margin-top: 0.2rem; + } + + >.text { display: block; flex: none; min-width: 0; @@ -121,7 +130,7 @@ wc-page.main-settings-page { color: var(--popup-text-color); } - > .value { + >.value { flex: none; font-size: 0.82rem; line-height: 1.35; @@ -154,40 +163,52 @@ wc-page.main-settings-page { } .settings-search-section .list-item { - > .container { + >.container { padding-right: 0.35rem; - gap: 0.18rem; + + > *:not(:first-child) { + margin-top: 0.18rem; + } } - > .setting-tail { + >.setting-tail { display: flex; align-items: center; justify-content: flex-end; flex-shrink: 0; min-height: 1.65rem; - gap: 0.32rem; margin-left: 0.9rem; align-self: center; + + > *:not(:first-child) { + margin-left: 0.32rem; + } } - &.has-subtitle > .container { - gap: 0.24rem; + &.has-subtitle>.container { padding-top: 0.14rem; padding-right: 0.6rem; + + > *:not(:first-child) { + margin-top: 0.24rem; + } } - &.has-subtitle.has-tail-select > .container { + &.has-subtitle.has-tail-select>.container { padding-right: 0.95rem; } - &.compact > .container { + &.compact>.container { align-self: center; justify-content: center; - gap: 0; padding-top: 0; + + > *:not(:first-child) { + margin-top: 0; + } } - &.compact > .setting-tail { + &.compact>.setting-tail { align-self: center; } } @@ -195,7 +216,6 @@ wc-page.main-settings-page { .settings-search-section .setting-value-display { display: inline-flex; align-items: center; - gap: 0.15rem; min-height: auto; padding: 0; border: none; @@ -205,24 +225,29 @@ wc-page.main-settings-page { color: color-mix(in srgb, var(--secondary-text-color), transparent 18%); box-sizing: border-box; + > *:not(:first-child) { + margin-left: 0.15rem; + } + &.is-select { min-width: clamp(6.75rem, 30vw, 8.5rem); max-width: min(45vw, 11.5rem); min-height: 2.35rem; padding: 0 0.8rem 0 0.95rem; - gap: 0.55rem; justify-content: space-between; border: 1px solid var(--border-color); border: 1px solid color-mix(in srgb, var(--border-color), transparent 12%); border-radius: 0.56rem; - background: color-mix( - in srgb, - var(--secondary-color), - var(--popup-background-color) 42% - ); + background: color-mix(in srgb, + var(--secondary-color), + var(--popup-background-color) 42%); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-color), transparent 44%), 0 1px 2px rgba(0, 0, 0, 0.12); + + > *:not(:first-child) { + margin-left: 0.55rem; + } } } @@ -298,11 +323,9 @@ wc-page.main-settings-page { border: 1px solid color-mix(in srgb, var(--border-color), transparent 6%); border-radius: 999px; background: var(--popup-background-color); - background: color-mix( - in srgb, - var(--secondary-color), - var(--popup-background-color) 30% - ); + background: color-mix(in srgb, + var(--secondary-color), + var(--popup-background-color) 30%); transition: background-color 160ms ease, border-color 160ms ease, @@ -314,11 +337,9 @@ wc-page.main-settings-page { margin: 0.14rem; border-radius: 999px; background: var(--popup-text-color); - background: color-mix( - in srgb, - var(--popup-text-color), - var(--popup-background-color) 18% - ); + background: color-mix(in srgb, + var(--popup-text-color), + var(--popup-background-color) 18%); opacity: 1; box-shadow: 0 0 0 1px color-mix(in srgb, var(--border-color), transparent 34%), @@ -330,37 +351,30 @@ wc-page.main-settings-page { } } - input:checked + .box { + input:checked+.box { background: var(--button-background-color); border-color: color-mix(in srgb, var(--button-background-color), transparent 10%); - box-shadow: inset 0 0 0 1px - color-mix(in srgb, var(--button-background-color), transparent 12%); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--button-background-color), transparent 12%); } - input:checked + .box::after { + input:checked+.box::after { transform: translateX(1.12rem); background: var(--button-text-color); opacity: 1; box-shadow: 0 2px 8px color-mix(in srgb, var(--button-background-color), transparent 55%); } - input:not(:checked) + .box::after { + input:not(:checked)+.box::after { opacity: 1; } } - .settings-search-section - .list-item.has-tail-select:focus - .setting-value-display.is-select, - .settings-search-section - .list-item.has-tail-select:active - .setting-value-display.is-select { + .settings-search-section .list-item.has-tail-select:focus .setting-value-display.is-select, + .settings-search-section .list-item.has-tail-select:active .setting-value-display.is-select { border-color: color-mix(in srgb, var(--active-color), transparent 48%); - background: color-mix( - in srgb, - var(--popup-background-color), - var(--active-color) 9% - ); + background: color-mix(in srgb, + var(--popup-background-color), + var(--active-color) 9%); } @media screen and (min-width: 768px) { @@ -392,8 +406,8 @@ wc-page.detail-settings-page { background: var(--secondary-color); @include settings-page-shell(".detail-settings-list"); - .detail-settings-list > .list-item, - .settings-section-card > .list-item { + .detail-settings-list>.list-item, + .settings-section-card>.list-item { display: flex; width: 100%; margin: 0; @@ -401,17 +415,22 @@ wc-page.detail-settings-page { min-height: 3.2rem; box-sizing: border-box; align-items: center; - gap: 0.85rem; background: transparent; cursor: pointer; transition: background-color 140ms ease; + > *:not(:first-child) { + margin-left: 0.85rem; + } + &.compact { align-items: center; } &.no-leading-icon { - gap: 0; + > *:not(:first-child) { + margin-left: 0; + } } &:not(:last-of-type) { @@ -425,14 +444,14 @@ wc-page.detail-settings-page { background: color-mix(in srgb, var(--secondary-color), var(--popup-text-color) 4%); } - > .icon.no-icon { + >.icon.no-icon { width: 0; min-width: 0; height: 0; margin: 0; } - > .icon:first-child:not(.no-icon) { + >.icon:first-child:not(.no-icon) { display: flex; align-items: center; justify-content: center; @@ -444,16 +463,19 @@ wc-page.detail-settings-page { color: color-mix(in srgb, var(--secondary-text-color), transparent 18%); } - > .container { + >.container { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: visible; - gap: 0.18rem; padding-right: 0.35rem; - > .text { + > *:not(:first-child) { + margin-top: 0.18rem; + } + + >.text { display: flex; align-items: center; flex: none; @@ -464,7 +486,7 @@ wc-page.detail-settings-page { color: var(--popup-text-color); } - > .value { + >.value { flex: none; font-size: 0.82rem; line-height: 1.35; @@ -478,47 +500,55 @@ wc-page.detail-settings-page { } } - > .setting-tail { + >.setting-tail { display: flex; align-items: center; justify-content: flex-end; flex-shrink: 0; min-height: 1.65rem; - gap: 0.32rem; margin-left: 0.9rem; align-self: center; + + > *:not(:first-child) { + margin-left: 0.32rem; + } } } - .detail-settings-list > .list-item.has-subtitle > .container, - .settings-section-card > .list-item.has-subtitle > .container { - gap: 0.24rem; + .detail-settings-list>.list-item.has-subtitle>.container, + .settings-section-card>.list-item.has-subtitle>.container { padding-top: 0.14rem; padding-right: 0.6rem; + + > *:not(:first-child) { + margin-top: 0.24rem; + } } - .detail-settings-list > .list-item.has-subtitle.has-tail-select > .container, - .settings-section-card > .list-item.has-subtitle.has-tail-select > .container { + .detail-settings-list>.list-item.has-subtitle.has-tail-select>.container, + .settings-section-card>.list-item.has-subtitle.has-tail-select>.container { padding-right: 0.95rem; } - .detail-settings-list > .list-item.compact > .container, - .settings-section-card > .list-item.compact > .container { + .detail-settings-list>.list-item.compact>.container, + .settings-section-card>.list-item.compact>.container { align-self: center; justify-content: center; - gap: 0; padding-top: 0; + + > *:not(:first-child) { + margin-top: 0; + } } - .detail-settings-list > .list-item.compact > .setting-tail, - .settings-section-card > .list-item.compact > .setting-tail { + .detail-settings-list>.list-item.compact>.setting-tail, + .settings-section-card>.list-item.compact>.setting-tail { align-self: center; } .setting-value-display { display: inline-flex; align-items: center; - gap: 0.15rem; min-height: auto; padding: 0; border: none; @@ -528,24 +558,29 @@ wc-page.detail-settings-page { color: color-mix(in srgb, var(--secondary-text-color), transparent 18%); box-sizing: border-box; + > *:not(:first-child) { + margin-left: 0.15rem; + } + &.is-select { min-width: clamp(6.75rem, 30vw, 8.5rem); max-width: min(45vw, 11.5rem); min-height: 2.35rem; padding: 0 0.8rem 0 0.95rem; - gap: 0.55rem; justify-content: space-between; border: 1px solid var(--border-color); border: 1px solid color-mix(in srgb, var(--border-color), transparent 12%); border-radius: 0.56rem; - background: color-mix( - in srgb, - var(--secondary-color), - var(--popup-background-color) 42% - ); + background: color-mix(in srgb, + var(--secondary-color), + var(--popup-background-color) 42%); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-color), transparent 44%), 0 1px 2px rgba(0, 0, 0, 0.12); + + > *:not(:first-child) { + margin-left: 0.55rem; + } } } @@ -619,11 +654,9 @@ wc-page.detail-settings-page { border: 1px solid color-mix(in srgb, var(--border-color), transparent 6%); border-radius: 999px; background: var(--popup-background-color); - background: color-mix( - in srgb, - var(--secondary-color), - var(--popup-background-color) 30% - ); + background: color-mix(in srgb, + var(--secondary-color), + var(--popup-background-color) 30%); transition: background-color 160ms ease, border-color 160ms ease, @@ -635,11 +668,9 @@ wc-page.detail-settings-page { margin: 0.14rem; border-radius: 999px; background: var(--popup-text-color); - background: color-mix( - in srgb, - var(--popup-text-color), - var(--popup-background-color) 18% - ); + background: color-mix(in srgb, + var(--popup-text-color), + var(--popup-background-color) 18%); opacity: 1; box-shadow: 0 0 0 1px var(--border-color), @@ -654,16 +685,15 @@ wc-page.detail-settings-page { } } - input:checked + .box { + input:checked+.box { background: var(--button-background-color); border-color: var(--button-background-color); border-color: color-mix(in srgb, var(--button-background-color), transparent 10%); box-shadow: inset 0 0 0 1px var(--button-background-color); - box-shadow: inset 0 0 0 1px - color-mix(in srgb, var(--button-background-color), transparent 12%); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--button-background-color), transparent 12%); } - input:checked + .box::after { + input:checked+.box::after { transform: translateX(1.12rem); background: var(--button-text-color); opacity: 1; @@ -671,7 +701,7 @@ wc-page.detail-settings-page { box-shadow: 0 2px 8px color-mix(in srgb, var(--button-background-color), transparent 55%); } - input:not(:checked) + .box::after { + input:not(:checked)+.box::after { opacity: 1; } } @@ -679,7 +709,6 @@ wc-page.detail-settings-page { .note { display: flex; align-items: flex-start; - gap: 0.6rem; width: auto; box-sizing: border-box; margin: 0.25rem 0.75rem; @@ -690,6 +719,10 @@ wc-page.detail-settings-page { background: var(--popup-background-color); background: color-mix(in srgb, var(--popup-background-color), var(--active-color) 8%); + > *:not(:first-child) { + margin-left: 0.6rem; + } + .note-title { display: flex; align-items: center; @@ -700,7 +733,7 @@ wc-page.detail-settings-page { background: transparent; text-transform: none; - > .icon { + >.icon { display: inline-flex; align-items: center; justify-content: center; @@ -714,7 +747,7 @@ wc-page.detail-settings-page { margin-top: 0.12rem; } - > span:last-child { + >span:last-child { display: none; } } @@ -744,28 +777,23 @@ wc-page.detail-settings-page { } } - .detail-settings-list > .list-item.has-tail-select:focus .setting-value-display.is-select, - .detail-settings-list > .list-item.has-tail-select:active .setting-value-display.is-select, - .settings-section-card - > .list-item.has-tail-select:focus - .setting-value-display.is-select, - .settings-section-card - > .list-item.has-tail-select:active - .setting-value-display.is-select { + .detail-settings-list>.list-item.has-tail-select:focus .setting-value-display.is-select, + .detail-settings-list>.list-item.has-tail-select:active .setting-value-display.is-select, + .settings-section-card>.list-item.has-tail-select:focus .setting-value-display.is-select, + .settings-section-card>.list-item.has-tail-select:active .setting-value-display.is-select { border-color: color-mix(in srgb, var(--active-color), transparent 48%); - background: color-mix( - in srgb, - var(--popup-background-color), - var(--active-color) 9% - ); + background: color-mix(in srgb, + var(--popup-background-color), + var(--active-color) 9%); } @include settings-icon-reset; } wc-page.detail-settings-page.formatter-settings-page { - .detail-settings-list > .list-item, - .settings-section-card > .list-item { + + .detail-settings-list>.list-item, + .settings-section-card>.list-item { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -781,7 +809,7 @@ wc-page.detail-settings-page.formatter-settings-page { padding-bottom: 0.74rem; } - > .icon:first-child:not(.no-icon) { + >.icon:first-child:not(.no-icon) { width: 1.1rem; min-width: 1.1rem; height: 1.1rem; @@ -789,19 +817,21 @@ wc-page.detail-settings-page.formatter-settings-page { font-size: 1rem; } - > .container { - gap: 0.2rem; + >.container { + > *:not(:first-child) { + margin-top: 0.2rem; + } } - > .container > .value { + >.container>.value { -webkit-line-clamp: unset; } } - .detail-settings-list > .list-item.compact > .container, - .settings-section-card > .list-item.compact > .container, - .detail-settings-list > .list-item.compact > .setting-tail, - .settings-section-card > .list-item.compact > .setting-tail { + .detail-settings-list>.list-item.compact>.container, + .settings-section-card>.list-item.compact>.container, + .detail-settings-list>.list-item.compact>.setting-tail, + .settings-section-card>.list-item.compact>.setting-tail { align-self: center; } -} +} \ No newline at end of file diff --git a/src/components/sideButton/style.scss b/src/components/sideButton/style.scss index efd92878f..79a85796d 100644 --- a/src/components/sideButton/style.scss +++ b/src/components/sideButton/style.scss @@ -32,4 +32,8 @@ display: flex; flex-direction: column; gap: 5px; + + @supports not (gap: 1px) { + > * + * { margin-top: 5px; } + } } \ No newline at end of file diff --git a/src/components/sidebar/style.scss b/src/components/sidebar/style.scss index 4a9b71c63..4ac877ecb 100644 --- a/src/components/sidebar/style.scss +++ b/src/components/sidebar/style.scss @@ -74,6 +74,11 @@ body.no-animation { flex-direction: column; align-items: center; gap: 4px; + + @supports not (gap: 1px) { + > * + * { margin-top: 4px; } + } + scrollbar-width: none; -ms-overflow-style: none; @@ -410,4 +415,8 @@ body.no-animation { background-color: var(--border-color); margin: 4px 0; } +} + +@supports not (gap: 1px) { + .user-menu-item > * + * { margin-left: 8px; } } \ No newline at end of file diff --git a/src/components/symbolsPanel/styles.scss b/src/components/symbolsPanel/styles.scss index e08b42c7d..b710725a1 100644 --- a/src/components/symbolsPanel/styles.scss +++ b/src/components/symbolsPanel/styles.scss @@ -406,4 +406,14 @@ flex-shrink: 0; opacity: 0.7; } +} + +@supports not (gap: 1px) { + .symbols-panel { + .header-content > * + * { margin-top: 2px; } + .header-title > * + * { margin-left: 8px; } + .header-actions > * + * { margin-left: 4px; } + .loading-state > * + * { margin-left: 12px; } + } + .symbol-item > * + * { margin-left: 10px; } } \ No newline at end of file diff --git a/src/components/terminal/terminal.js b/src/components/terminal/terminal.js index 0271b4a40..b1a82a9fa 100644 --- a/src/components/terminal/terminal.js +++ b/src/components/terminal/terminal.js @@ -702,23 +702,25 @@ export default class TerminalComponent { rows: this.terminal.rows, }; - const response = await fetch( - `http://localhost:${this.options.port}/terminals`, - { - method: "POST", - headers: { - "Content-Type": "application/json", + const response = await new Promise((resolve, reject) => { + cordova.plugin.http.sendRequest( + `http://localhost:${this.options.port}/terminals`, + { + method: "POST", + responseType: "text", + serializer: "json", + data: requestBody, }, - body: JSON.stringify(requestBody), - }, - ); + (res) => resolve(res), + (err) => reject(new Error(err.error || `HTTP error!`)), + ); + }); - if (!response.ok) { + if (response.status < 200 || response.status >= 300) { throw new Error(`HTTP error! status: ${response.status}`); } - const data = await response.text(); - this.pid = data.trim(); + this.pid = response.data.trim(); return this.pid; } catch (error) { console.error("Failed to create terminal session:", error); @@ -847,16 +849,18 @@ export default class TerminalComponent { if (!this.pid || !this.serverMode) return; try { - await fetch( - `http://localhost:${this.options.port}/terminals/${this.pid}/resize`, - { - method: "POST", - headers: { - "Content-Type": "application/json", + await new Promise((resolve, reject) => { + cordova.plugin.http.sendRequest( + `http://localhost:${this.options.port}/terminals/${this.pid}/resize`, + { + method: "POST", + serializer: "json", + data: { cols, rows }, }, - body: JSON.stringify({ cols, rows }), - }, - ); + (res) => resolve(res), + (err) => reject(err), + ); + }); } catch (error) { console.error("Failed to resize terminal:", error); } @@ -1131,12 +1135,16 @@ export default class TerminalComponent { if (this.pid && this.serverMode) { try { - await fetch( - `http://localhost:${this.options.port}/terminals/${this.pid}/terminate`, - { - method: "POST", - }, - ); + await new Promise((resolve, reject) => { + cordova.plugin.http.sendRequest( + `http://localhost:${this.options.port}/terminals/${this.pid}/terminate`, + { + method: "POST", + }, + (res) => resolve(res), + (err) => reject(err), + ); + }); } catch (error) { console.error("Failed to terminate terminal:", error); } @@ -1189,7 +1197,7 @@ export default class TerminalComponent { // Internal helpers for WebGL renderer lifecycle TerminalComponent.prototype._handleWebglContextLoss = function () { try { - console.warn("WebGL context lost; falling back to canvas renderer"); + console.warn("WebGL context lost; terminal rendering will be degraded"); try { this.webglAddon?.dispose?.(); } catch {} diff --git a/src/components/terminal/terminalManager.js b/src/components/terminal/terminalManager.js index f9d22dd2d..820565e59 100644 --- a/src/components/terminal/terminalManager.js +++ b/src/components/terminal/terminalManager.js @@ -3,18 +3,18 @@ * Handles terminal session creation and management */ -import EditorFile from "lib/editorFile"; -import TerminalComponent from "./terminal"; -import TerminalTouchSelection from "./terminalTouchSelection"; import "@xterm/xterm/css/xterm.css"; import quickTools from "components/quickTools"; import toast from "components/toast"; import alert from "dialogs/alert"; import confirm from "dialogs/confirm"; +import EditorFile from "lib/editorFile"; import openFile from "lib/openFile"; import openFolder from "lib/openFolder"; import appSettings from "lib/settings"; import helpers from "utils/helpers"; +import TerminalComponent from "./terminal"; +import TerminalTouchSelection from "./terminalTouchSelection"; const TERMINAL_SESSION_STORAGE_KEY = "acodeTerminalSessions"; @@ -581,26 +581,34 @@ class TerminalManager { const textarea = terminalComponent.terminal?.textarea; if (textarea) { const onFocus = () => { - const { $toggler } = quickTools; - $toggler.classList.add("hide"); - clearTimeout(this.togglerTimeout); - this.togglerTimeout = setTimeout(() => { - $toggler.style.display = "none"; - }, 300); + clearTimeout(this.onBlurTimeout); + this.onFocusTimeout = setTimeout(() => { + const { $toggler } = quickTools; + $toggler.classList.add("hide"); + clearTimeout(this.quickToolsTogglerTimeout); + this.quickToolsTogglerTimeout = setTimeout(() => { + $toggler.style.display = "none"; + }, 300); + }, 100); }; const onBlur = () => { - const { $toggler } = quickTools; - clearTimeout(this.togglerTimeout); - $toggler.style.display = ""; - setTimeout(() => { - $toggler.classList.remove("hide"); - }, 10); + clearTimeout(this.onFocusTimeout); + this.onBlurTimeout = setTimeout(() => { + const { $toggler } = quickTools; + $toggler.style.display = ""; + clearTimeout(this.quickToolsTogglerTimeout); + requestAnimationFrame(() => $toggler.classList.remove("hide")); + }, 100); }; textarea.addEventListener("focus", onFocus); textarea.addEventListener("blur", onBlur); + if (textarea === document.activeElement) { + onFocus(); + } + terminalComponent.cleanupFocusHandlers = () => { textarea.removeEventListener("focus", onFocus); textarea.removeEventListener("blur", onBlur); diff --git a/src/handlers/keyboard.js b/src/handlers/keyboard.js index 7dba9e920..9293d0f7e 100644 --- a/src/handlers/keyboard.js +++ b/src/handlers/keyboard.js @@ -1,3 +1,4 @@ +import { bannerAd } from "lib/startAd"; import { getSystemConfiguration, HARDKEYBOARDHIDDEN_NO, @@ -124,6 +125,8 @@ document.addEventListener("deviceready", () => { } } + toggleBannerAd(currentWindowHeight >= window.innerHeight); + currentWindowHeight = window.innerHeight; }); @@ -148,7 +151,6 @@ document.addEventListener("deviceready", () => { } focusBlurEditor(keyboardHiddenYes); - showHideAd(keyboardHiddenYes); }); }); @@ -199,16 +201,16 @@ function focusBlurEditor(keyboardHidden) { * Show ad if keyboard is hidden and ad is active, hide ad otherwise. * @param {boolean} keyboardHidden */ -function showHideAd(keyboardHidden) { - const bannerIsActive = !!window.ad?.active; +function toggleBannerAd(keyboardHidden) { + const bannerIsActive = !!bannerAd?.active; if ( !keyboardHidden && bannerIsActive && - typeof window.ad?.hide === "function" + typeof bannerAd?.hide === "function" ) { - window.ad.hide(); - } else if (bannerIsActive && typeof window.ad?.show === "function") { - window.ad.show(); + bannerAd.hide(); + } else if (bannerIsActive && typeof bannerAd?.show === "function") { + bannerAd.show(); } } diff --git a/src/lib/adRewards.js b/src/lib/adRewards.js index ef3d94420..38a2450e0 100644 --- a/src/lib/adRewards.js +++ b/src/lib/adRewards.js @@ -2,6 +2,7 @@ import toast from "components/toast"; import auth from "./auth"; import config from "./config"; import secureAdRewardState from "./secureAdRewardState"; +import { adUnitIdRewarded, bannerAd } from "./startAd"; const ONE_HOUR = 60 * 60 * 1000; const MAX_TIMEOUT = 2_147_483_647; @@ -67,10 +68,6 @@ function formatDurationRange(minDurationMs, maxDurationMs) { return `${minHours}-${maxHours} hours`; } -function getRewardedUnitId() { - return window.adRewardedUnitId || ""; -} - function getExpiryDate() { return state.adFreeUntil ? new Date(state.adFreeUntil) : null; } @@ -90,9 +87,9 @@ function emitChange() { } function hideActiveBanner() { - if (window.ad?.active) { - window.ad.active = false; - window.ad.hide?.(); + if (bannerAd?.active) { + bannerAd.active = false; + bannerAd?.hide(); } } @@ -187,8 +184,7 @@ async function getRewardIdentity() { } async function createRewardedAd(offer, step, sessionId) { - const rewardedUnitId = getRewardedUnitId(); - if (!rewardedUnitId || !admob?.RewardedAd) { + if (!admob?.RewardedAd) { throw new Error("Rewarded ads are not available in this build."); } @@ -201,7 +197,7 @@ async function createRewardedAd(offer, step, sessionId) { ].join("&"); return new admob.RewardedAd({ - adUnitId: rewardedUnitId, + adUnitId: adUnitIdRewarded, serverSideVerification: { userId, customData, @@ -319,7 +315,7 @@ export default { return Boolean(!config.HAS_PRO && !this.isAdFreeActive()); }, isRewardedSupported() { - return Boolean(!config.HAS_PRO && admob?.RewardedAd && getRewardedUnitId()); + return Boolean(!config.HAS_PRO && admob?.RewardedAd && adUnitIdRewarded); }, getRewardedUnavailableReason() { if (config.HAS_PRO) { @@ -328,9 +324,6 @@ export default { if (!admob?.RewardedAd) { return "Rewarded ads are unavailable on this device."; } - if (!getRewardedUnitId()) { - return "Rewarded ads are not configured for production yet."; - } return ""; }, canRedeemNow() { diff --git a/src/lib/devTools.js b/src/lib/devTools.js index 098e48c25..cc8d90789 100644 --- a/src/lib/devTools.js +++ b/src/lib/devTools.js @@ -53,7 +53,8 @@ const devTools = { "utf-8", ); await fsOperation(DATA_STORAGE).createFile("eruda.js", erudaScript); - } catch { + } catch (e) { + console.error("Failed to download eruda.js", e); } finally { if (showLoader) loader.destroy(); } diff --git a/src/lib/fonts.js b/src/lib/fonts.js index 14d8b2197..fbd8c2e8b 100644 --- a/src/lib/fonts.js +++ b/src/lib/fonts.js @@ -294,9 +294,10 @@ async function loadFont(name) { // Replace any pre-injected @font-face block (from injectFontFace) // with the locally-cached version, or append if not yet present if ($style.textContent.includes(`font-family: '${name}'`)) { + let escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); $style.textContent = $style.textContent.replace( new RegExp( - `@font-face\\s*\\{[^}]*font-family:\\s*'${name}'[^}]*\\}`, + `@font-face\\s*\\{[^}]*font-family:\\s*'${escapedName}'[^}]*\\}`, "g", ), css, diff --git a/src/lib/polyfill.js b/src/lib/polyfill.js index 3ed91597b..d8cab0bbb 100644 --- a/src/lib/polyfill.js +++ b/src/lib/polyfill.js @@ -102,6 +102,36 @@ }); })([Element.prototype, CharacterData.prototype, DocumentType.prototype]); +// polyfill for replaceChildren + +(function (arr) { + arr.forEach(function (item) { + if (item.hasOwnProperty("replaceChildren")) { + return; + } + Object.defineProperty(item, "replaceChildren", { + configurable: true, + enumerable: false, + writable: true, + value: function replaceChildren() { + while (this.firstChild) { + this.removeChild(this.firstChild); + } + // biome-ignore lint/style/useForOf: ES5 polyfill cannot use for...of + for (var i = 0; i < arguments.length; i++) { + var node = arguments[i]; + if (typeof node !== "object") { + node = this.ownerDocument.createTextNode(String(node)); + } else if (node.parentNode) { + node.parentNode.removeChild(node); + } + this.appendChild(node); + } + }, + }); + }); +})([Element.prototype, Document.prototype, DocumentFragment.prototype]); + // polyfill for toggleAttribute (function (arr) { diff --git a/src/lib/remoteStorage.js b/src/lib/remoteStorage.js index acfdd00d5..5185de0ce 100644 --- a/src/lib/remoteStorage.js +++ b/src/lib/remoteStorage.js @@ -6,6 +6,7 @@ import multiPrompt from "dialogs/multiPrompt"; import URLParse from "url-parse"; import helpers from "utils/helpers"; import Url from "utils/Url"; +import { interstitialAd } from "./startAd"; export default { /** @@ -426,9 +427,9 @@ export default { async function loadAd() { if (!helpers.canShowAds()) return; try { - if (!(await window.iad?.isLoaded())) { + if (!(await interstitialAd?.isLoaded())) { toast(strings.loading); - await window.iad.load(); + await interstitialAd?.load(); } } catch (error) { console.warn("Failed to load interstitial ad.", error); diff --git a/src/lib/startAd.js b/src/lib/startAd.js index dadbee960..c0e58a958 100644 --- a/src/lib/startAd.js +++ b/src/lib/startAd.js @@ -1,65 +1,81 @@ -import tag from "html-tag-js"; +import { BannerAd, InterstitialAd } from "plugins/admob/esm"; import config from "./config"; -let adUnitIdBanner = "ca-app-pub-5911839694379275/9157899592"; // Production -let adUnitIdInterstitial = "ca-app-pub-5911839694379275/9570937608"; // Production -let adUnitIdRewarded = "ca-app-pub-5911839694379275/1633667633"; // Production -let initialized = false; +export let adUnitIdBanner = "ca-app-pub-5911839694379275/9157899592"; // Production +export let adUnitIdInterstitial = "ca-app-pub-5911839694379275/9570937608"; // Production +export let adUnitIdRewarded = "ca-app-pub-5911839694379275/1633667633"; // Production +export let initialized = false; + +/** @type {BannerAd} */ +export let bannerAd = null; +/** @type {InterstitialAd} */ +export let interstitialAd = null; export default async function startAd() { if (config.HAS_PRO || typeof admob === "undefined") return; - if (!initialized) { - initialized = true; - - if (BuildInfo.buildType === "debug") { - console.info("!!! Using test ads"); - adUnitIdBanner = "ca-app-pub-3940256099942544/6300978111"; // Test - adUnitIdInterstitial = "ca-app-pub-3940256099942544/1033173712"; // Test - adUnitIdRewarded = "ca-app-pub-3940256099942544/5224354917"; // Test - } + if (window.ANDROID_SDK_INT < 29) { + console.warn("AdMob not supported on this Android version, skipping ads"); + return; } - const consentStatus = await consent.getConsentStatus(); - if (consentStatus === consent.ConsentStatus.Required) { - await consent.requestInfoUpdate(); - } + try { + if (!initialized) { + initialized = true; - const formStatus = await consent.getFormStatus(); - if (formStatus === consent.FormStatus.Available) { - const form = await consent.loadForm(); - form.show(); - } + if (BuildInfo.buildType === "debug") { + console.info("!!! Using test ads"); + adUnitIdBanner = "ca-app-pub-3940256099942544/6300978111"; // Test + adUnitIdInterstitial = "ca-app-pub-3940256099942544/1033173712"; // Test + adUnitIdRewarded = "ca-app-pub-3940256099942544/5224354917"; // Test + } + } - await admob.start(); + const consentStatus = await consent.getConsentStatus(); + if (consentStatus === consent.ConsentStatus.Required) { + await consent.requestInfoUpdate(); + } - const currentHour = new Date().getHours(); - //currentHour >= 22: Covers 10:00 PM to 11:59 PM. - //currentHour < 4: Covers 12:00 AM to 3:59 AM. - const isQuietHours = currentHour >= 22 || currentHour < 4; + const formStatus = await consent.getFormStatus(); + if (formStatus === consent.FormStatus.Available) { + const form = await consent.loadForm(); + form.show(); + } - await admob.configure({ - appMuted: isQuietHours, - appVolume: isQuietHours ? 0.0 : 1.0, - }); + await admob.start(); - const banner = new admob.BannerAd({ - adUnitId: adUnitIdBanner, - position: "bottom", - }); + const currentHour = new Date().getHours(); + const isQuietHours = currentHour >= 22 || currentHour < 4; - const interstitial = new admob.InterstitialAd({ - adUnitId: adUnitIdInterstitial, - }); + await admob.configure({ + appMuted: isQuietHours, + appVolume: isQuietHours ? 0.0 : 1.0, + }); - interstitial.load(); + const banner = new admob.BannerAd({ + adUnitId: adUnitIdBanner, + position: "bottom", + }); + + const interstitial = new admob.InterstitialAd({ + adUnitId: adUnitIdInterstitial, + }); - interstitial.on("dismiss", () => { interstitial.load(); - }); - window.ad = banner; - window.iad = interstitial; - window.adRewardedUnitId = adUnitIdRewarded; + + interstitial.on("dismiss", () => { + interstitial.load(); + }); + + bannerAd = banner; + interstitialAd = interstitial; + window.ad = banner; + window.iad = interstitial; + window.adRewardedUnitId = adUnitIdRewarded; + } catch (error) { + console.error("Failed to initialize ads:", error); + initialized = false; + } } /** @@ -67,14 +83,13 @@ export default async function startAd() { * @param {Boolean} [force=false] */ export function hideAd(force = false) { - const { ad } = window; - if (ad?.active && typeof ad.hide === "function") { - const $pages = tag.getAll(".page-replacement"); - const hide = $pages.length === 1; - - if (force || hide) { - ad.active = false; - ad.hide(); - } + if (!bannerAd?.active || typeof bannerAd.hide !== "function") return; + + const $pages = tag.getAll(".page-replacement"); + const hide = $pages.length === 1; + + if (force || hide) { + bannerAd.active = false; + bannerAd.hide(); } } diff --git a/src/main.js b/src/main.js index 3f0fcf543..33b2fab5f 100644 --- a/src/main.js +++ b/src/main.js @@ -217,6 +217,7 @@ async function onDeviceReady() { const { versionCode } = BuildInfo; if ( + previousVersionCode != null && !Number.isNaN(previousVersionCode) && previousVersionCode !== versionCode ) { diff --git a/src/main.scss b/src/main.scss index eab98f5d9..1e1216240 100644 --- a/src/main.scss +++ b/src/main.scss @@ -792,6 +792,13 @@ input[type="search"]::-webkit-search-results-decoration { } } + @supports not (gap: 1px) { + > * + * { margin-top: 8px; } + .notification-toast { + > * + * { margin-left: 12px; } + } + } + @media (max-width: 768px) { align-items: stretch; diff --git a/src/pages/about/about.scss b/src/pages/about/about.scss index 6b66771e0..8b19263a7 100644 --- a/src/pages/about/about.scss +++ b/src/pages/about/about.scss @@ -114,6 +114,11 @@ } } + @supports not (gap: 1px) { + > * + * { margin-top: 24px; } + .social-links > * { margin-right: 12px; margin-bottom: 12px; } + } + .icon { height: 100%; width: 100%; diff --git a/src/pages/adRewards/adRewards.scss b/src/pages/adRewards/adRewards.scss index 977d5c551..caadfa1f6 100644 --- a/src/pages/adRewards/adRewards.scss +++ b/src/pages/adRewards/adRewards.scss @@ -242,6 +242,14 @@ } } + @supports not (gap: 1px) { + .reward-status > * + * { margin-left: 12px; } + .reward-grid > * { margin-right: 10px; margin-bottom: 10px; } + .reward-offer > * + * { margin-top: 8px; } + .offer-header > * + * { margin-left: 8px; } + .reward-notes > * + * { margin-top: 10px; } + } + @media (max-width: 400px) { #ad-rewards-page .reward-grid { grid-template-columns: 1fr; diff --git a/src/pages/changelog/style.scss b/src/pages/changelog/style.scss index e5bc0ddf8..a7827d3ce 100644 --- a/src/pages/changelog/style.scss +++ b/src/pages/changelog/style.scss @@ -23,7 +23,11 @@ } } -.status-indicator { + @supports not (gap: 1px) { + .changelog-version-selector > * + * { margin-left: 8px; } + } + + .status-indicator { display: inline-block; width: 8px; height: 8px; diff --git a/src/pages/fileBrowser/fileBrowser.scss b/src/pages/fileBrowser/fileBrowser.scss index 2812d035c..8e64a9622 100644 --- a/src/pages/fileBrowser/fileBrowser.scss +++ b/src/pages/fileBrowser/fileBrowser.scss @@ -120,6 +120,10 @@ z-index: 1; gap: 10px; + @supports not (gap: 1px) { + > * + * { margin-left: 10px; } + } + .selection-count { font-size: 0.9em; } diff --git a/src/pages/fontManager/style.scss b/src/pages/fontManager/style.scss index d67e1b0ab..b3f25a635 100644 --- a/src/pages/fontManager/style.scss +++ b/src/pages/fontManager/style.scss @@ -112,7 +112,15 @@ wc-page.font-manager-page { font-weight: 700; } - @media screen and (min-width: 768px) { + @supports not (gap: 1px) { + .font-manager-list > .list-item { + > * + * { margin-left: 0.85rem; } + > .container > * + * { margin-top: 0.24rem; } + > .setting-tail > * + * { margin-left: 0.65rem; } + } + } + + @media screen and (min-width: 768px) { .font-manager-list { padding-left: 0.5rem; padding-right: 0.5rem; diff --git a/src/pages/markdownPreview/style.scss b/src/pages/markdownPreview/style.scss index 53dfd9a99..4e666c17f 100644 --- a/src/pages/markdownPreview/style.scss +++ b/src/pages/markdownPreview/style.scss @@ -108,6 +108,10 @@ color: var(--primary-text-color) !important; } + @supports not (gap: 1px) { + section.eqno > * + * { margin-left: 12px; } + } + .mermaid-error { border: 1px solid var(--danger-color); border-radius: 8px; diff --git a/src/pages/plugin/plugin.js b/src/pages/plugin/plugin.js index 165460284..bbb32ad8c 100644 --- a/src/pages/plugin/plugin.js +++ b/src/pages/plugin/plugin.js @@ -12,7 +12,7 @@ import customTab from "lib/customTab"; import installPlugin from "lib/installPlugin"; import InstallState from "lib/installState"; import settings from "lib/settings"; -import { hideAd } from "lib/startAd"; +import { hideAd, interstitialAd } from "lib/startAd"; import markdownIt from "markdown-it"; import anchor from "markdown-it-anchor"; import markdownItFootnote from "markdown-it-footnote"; @@ -563,10 +563,10 @@ export default async function PluginInclude( async function loadAd(el) { if (!helpers.canShowAds()) return; try { - if (!(await window.iad?.isLoaded())) { + if (!(await interstitialAd?.isLoaded())) { const oldText = el.textContent; el.textContent = strings["loading..."]; - await window.iad.load(); + await interstitialAd?.load(); el.textContent = oldText; } } catch (error) { diff --git a/src/pages/plugin/plugin.scss b/src/pages/plugin/plugin.scss index 2b6c2656b..84abb2aa1 100644 --- a/src/pages/plugin/plugin.scss +++ b/src/pages/plugin/plugin.scss @@ -357,6 +357,28 @@ } } + @supports not (gap: 1px) { + .plugin-header > * { + margin-right: 20px; + margin-bottom: 20px; + } + .plugin-header > *:last-child { + margin-right: 0; + margin-bottom: 0; + } + .title-wrapper > * + * { margin-left: 16px; } + .source-indicator > * + * { margin-left: 6px; } + .plugin-meta > * + * { margin-left: 16px; } + .meta-item > * + * { margin-left: 4px; } + .metrics-row > * + * { margin-left: 16px; } + .metric > * + * { margin-left: 4px; } + .keywords > * + * { margin-left: 6px; } + .action-buttons > * + * { margin-left: 8px; } + .info:not(.icon) > * + * { margin-left: 5px; } + .btn > * + * { margin-left: 6px; } + .contributor > * + * { margin-left: 12px; } + } + @media (max-width: 768px) { .plugin-header { grid-template-columns: 1fr; diff --git a/src/pages/plugin/plugin.view.js b/src/pages/plugin/plugin.view.js index b02f1a05a..658cee44f 100644 --- a/src/pages/plugin/plugin.view.js +++ b/src/pages/plugin/plugin.view.js @@ -157,7 +157,7 @@ export default (props) => {
= 80 ? "rating-high" : rating.replace("%", "") >= 50 ? "" : "rating-low"}`} + className={`rating-value ${rating === "unrated" ? "" : rating.replace("%", "") >= 80 ? "rating-high" : rating.replace("%", "") >= 50 ? "rating-medium" : "rating-low"}`} > {rating} diff --git a/src/pages/plugins/plugins.scss b/src/pages/plugins/plugins.scss index d8b3635aa..37860ddbb 100644 --- a/src/pages/plugins/plugins.scss +++ b/src/pages/plugins/plugins.scss @@ -293,6 +293,15 @@ box-shadow 180ms ease; } + @supports not (gap: 1px) { + .plugin-header > * + * { margin-left: 0.75rem; } + .plugin-info > * + * { margin-left: 0.75rem; } + .plugin-title > * + * { margin-left: 0.75rem; } + .plugin-meta > * + * { margin-left: 0.5rem; } + .plugin-stats > * + * { margin-left: 0.375rem; } + .plugin-price > * + * { margin-left: 0.25rem; } + } + .plugin-toggle-switch[data-enabled="true"] .plugin-toggle-thumb, .plugin-toggle-track[data-enabled="true"] .plugin-toggle-thumb { transform: translateX(1.12rem); diff --git a/src/pages/quickTools/style.scss b/src/pages/quickTools/style.scss index 674a19326..4503198c4 100644 --- a/src/pages/quickTools/style.scss +++ b/src/pages/quickTools/style.scss @@ -128,6 +128,10 @@ } } + @supports not (gap: 1px) { + .quicktools-grid > * { margin-right: 8px; margin-bottom: 8px; } + } + .tool-ghost { position: fixed; top: 0; diff --git a/src/pages/sponsor/style.scss b/src/pages/sponsor/style.scss index b1e538c38..dc2c16f7d 100644 --- a/src/pages/sponsor/style.scss +++ b/src/pages/sponsor/style.scss @@ -106,6 +106,10 @@ line-height: 1.4; } + @supports not (gap: 1px) { + .tier-header > * + * { margin-left: 0.5rem; } + } + .purchase-btn { background: var(--button-background-color); color: var(--button-text-color); diff --git a/src/pages/sponsors/style.scss b/src/pages/sponsors/style.scss index eb5f6ab89..60d778430 100644 --- a/src/pages/sponsors/style.scss +++ b/src/pages/sponsors/style.scss @@ -95,6 +95,10 @@ } } + @supports not (gap: 1px) { + .sponsors > * { margin-right: 1rem; margin-bottom: 1rem; } + } + .sponsor-card { flex-grow: 1; min-width: 150px; diff --git a/src/pages/themeSetting/themeSetting.scss b/src/pages/themeSetting/themeSetting.scss index d89b7ad7e..789edbb5b 100644 --- a/src/pages/themeSetting/themeSetting.scss +++ b/src/pages/themeSetting/themeSetting.scss @@ -31,6 +31,7 @@ display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr); grid-template-rows: repeat(2, minmax(0, 1fr)); + gap: 1px; width: 1.5rem; min-width: 1.5rem; height: 1.5rem; @@ -52,4 +53,17 @@ .theme-swatch-main { grid-row: 1 / 3; } + + @supports not (gap: 1px) { + .theme-swatch-main { + margin-right: 1px; + } + .theme-swatch:nth-child(2) { + margin-left: 1px; + margin-bottom: 1px; + } + .theme-swatch:nth-child(3) { + margin-left: 1px; + } + } } diff --git a/src/pages/welcome/welcome.scss b/src/pages/welcome/welcome.scss index ae94a415d..5a1a95840 100644 --- a/src/pages/welcome/welcome.scss +++ b/src/pages/welcome/welcome.scss @@ -148,6 +148,14 @@ } } + @supports not (gap: 1px) { + .welcome-header > * + * { margin-left: 16px; } + .action-list > * + * { margin-top: 2px; } + .action-row > * + * { margin-left: 12px; } + .link-row > * { margin-right: 8px; margin-bottom: 8px; } + .link-item > * + * { margin-left: 8px; } + } + // Responsive adjustments for smaller screens @media (max-width: 360px) { #welcome-tab { diff --git a/src/plugins/admob/src/android/cordova/ads/Banner.kt b/src/plugins/admob/src/android/cordova/ads/Banner.kt index aea50f96b..57460f2b8 100644 --- a/src/plugins/admob/src/android/cordova/ads/Banner.kt +++ b/src/plugins/admob/src/android/cordova/ads/Banner.kt @@ -4,7 +4,6 @@ import admob.plus.cordova.Events import admob.plus.cordova.ExecuteContext import admob.plus.core.buildAdSize import admob.plus.core.pxToDp -import android.annotation.SuppressLint import android.content.res.Configuration import android.util.Log import android.view.Gravity @@ -12,7 +11,6 @@ import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.FrameLayout -import android.widget.LinearLayout import android.widget.RelativeLayout import com.google.android.gms.ads.AdListener import com.google.android.gms.ads.AdSize @@ -24,6 +22,7 @@ enum class AdSizeType { BANNER, LARGE_BANNER, MEDIUM_RECTANGLE, FULL_BANNER, LEADERBOARD, SMART_BANNER; companion object { + @Suppress("DEPRECATION") fun getAdSize(adSize: Int): AdSize? { return when (values()[adSize]) { BANNER -> AdSize.BANNER @@ -32,7 +31,6 @@ enum class AdSizeType { FULL_BANNER -> AdSize.FULL_BANNER LEADERBOARD -> AdSize.LEADERBOARD SMART_BANNER -> AdSize.SMART_BANNER - else -> null } } } @@ -131,12 +129,9 @@ class Banner(ctx: ExecuteContext) : AdBase(ctx) { } else if (mAdView!!.visibility == View.GONE) { mAdView!!.resume() mAdView!!.visibility = View.VISIBLE - } else { + // Re-apply bottom margin in case it was reset by hide() val wvParentView = getParentView(webView) - if (rootLinearLayout !== wvParentView && webViewWrapper !== wvParentView) { - removeFromParentView(rootLinearLayout) - addBannerView() - } + setBottomMargin(wvParentView) } ctx.resolve() } @@ -145,6 +140,8 @@ class Banner(ctx: ExecuteContext) : AdBase(ctx) { if (mAdView != null) { mAdView!!.pause() mAdView!!.visibility = View.GONE + val wvParentView = getParentView(webView) + resetBottomMargin(wvParentView) } ctx.resolve() } @@ -215,63 +212,34 @@ class Banner(ctx: ExecuteContext) : AdBase(ctx) { private fun addBannerView() { if (mAdView == null) return if (offset == null) { - if (getParentView(mAdView) === rootLinearLayout && rootLinearLayout != null) return + if (getParentView(mAdView) === plugin.contentView && plugin.contentView != null) return addBannerViewWithLinearLayout() } else { if (getParentView(mAdView) === mRelativeLayout && mRelativeLayout != null) return addBannerViewWithRelativeLayout() } plugin.contentView?.let { - it.bringToFront() it.requestLayout() - it.requestFocus() } } private fun addBannerViewWithLinearLayout() { val wvParentView = getParentView(webView) - if (rootLinearLayout == null) { - rootLinearLayout = LinearLayout(plugin.activity) - } - if (wvParentView != null && wvParentView !== rootLinearLayout && wvParentView !== webViewWrapper) { - if (webViewWrapper == null) { - webViewWrapper = FrameLayout(plugin.activity) - } else { - removeFromParentView(webViewWrapper) - } - wvParentView.removeView(webView) - val content = rootLinearLayout as LinearLayout? - content!!.orientation = LinearLayout.VERTICAL - rootLinearLayout!!.layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - 0.0f - ) - webViewWrapper!!.layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, + if (wvParentView == null) return + // Keep the WebView in its original parent. Add the banner to + // contentView as a bottom-aligned sibling and push the WebView's + // parent up via bottom margin — no removeView/reparent needed. + removeFromParentView(mAdView) + val content = plugin.contentView + if (content is FrameLayout) { + val bannerParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - 1.0f + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM ) - webViewWrapper!!.addView(webView) - rootLinearLayout!!.addView(webViewWrapper) - val view = getParentView(rootLinearLayout) - if (view !== wvParentView) { - removeFromParentView(rootLinearLayout) - wvParentView.addView(rootLinearLayout) - } - } - removeFromParentView(mAdView) - if (isPositionTop) { - rootLinearLayout!!.addView(mAdView, 0) - } else { - rootLinearLayout!!.addView(mAdView) - } - plugin.contentView?.let { - for (i in 0 until it.childCount) { - val view = it.getChildAt(i) - (view as? RelativeLayout)?.bringToFront() - } + content.addView(mAdView, bannerParams) } + setBottomMargin(wvParentView) } private fun addBannerViewWithRelativeLayout() { @@ -302,24 +270,25 @@ class Banner(ctx: ExecuteContext) : AdBase(ctx) { private val isPositionTop: Boolean get() = gravity == Gravity.TOP + private fun setBottomMargin(view: View?) { + val lp = view?.layoutParams as? FrameLayout.LayoutParams ?: return + lp.bottomMargin = adSize.getHeightInPixels(plugin.activity) + view.layoutParams = lp + } + + private fun resetBottomMargin(view: View?) { + val lp = view?.layoutParams as? FrameLayout.LayoutParams ?: return + if (lp.bottomMargin > 0) { + lp.bottomMargin = 0 + view.layoutParams = lp + } + } + companion object { private const val TAG = "AdMobPlus.Banner" - @SuppressLint("StaticFieldLeak") - private var rootLinearLayout: ViewGroup? = null - @SuppressLint("StaticFieldLeak") - private var webViewWrapper: FrameLayout? = null private var screenWidth = 0 - fun destroyParentView() { - try { - webViewWrapper?.removeAllViews() - webViewWrapper = null - val vg = getParentView(rootLinearLayout) - vg?.removeAllViews() - } finally { - rootLinearLayout = null - } - } + fun destroyParentView() {} private fun runJustBeforeBeingDrawn(view: View, runnable: Runnable) { val preDrawListener: ViewTreeObserver.OnPreDrawListener = diff --git a/src/plugins/admob/src/android/core/util.kt b/src/plugins/admob/src/android/core/util.kt index 10c616431..3b912dd7d 100644 --- a/src/plugins/admob/src/android/core/util.kt +++ b/src/plugins/admob/src/android/core/util.kt @@ -22,11 +22,11 @@ import kotlin.math.roundToInt fun buildAdRequest(opts: JSONObject): AdRequest { val builder = AdRequest.Builder() - opts.optString("contentUrl", null)?.let { + opts.optString("contentUrl", null as String?)?.let { builder.setContentUrl(it) } val extras = Bundle().apply { - opts.optString("npa", null)?.let { npa -> + opts.optString("npa", null as String?)?.let { npa -> putString("npa", npa) } } @@ -36,11 +36,13 @@ fun buildAdRequest(opts: JSONObject): AdRequest { fun buildAdSize(opts: JSONObject, activity: Activity): AdSize { val name = "size" if (!opts.has(name)) { + @Suppress("DEPRECATION") return AdSize.SMART_BANNER } val adSizeObj = opts.optJSONObject(name) val adSize = AdSizeType.getAdSize(opts.optInt(name)) if (adSizeObj == null) { + @Suppress("DEPRECATION") return adSize ?: AdSize.SMART_BANNER } val adaptive = adSizeObj.optString("adaptive") @@ -84,7 +86,7 @@ fun optFloat(opts: JSONObject, name: String): Float? { fun buildRequestConfiguration(opts: JSONObject): RequestConfiguration { val builder = RequestConfiguration.Builder() - opts.optString("maxAdContentRating", null)?.let { + opts.optString("maxAdContentRating", null as String?)?.let { builder.setMaxAdContentRating(it) } optBooleanToInt( diff --git a/src/plugins/system/www/plugin.js b/src/plugins/system/www/plugin.js index 519b4f988..e5b26da07 100644 --- a/src/plugins/system/www/plugin.js +++ b/src/plugins/system/www/plugin.js @@ -222,7 +222,7 @@ module.exports = { onFail, 'System', 'set-native-context-menu-disabled', - [String(!!disabled)], + [String(!!disabled)] ); }, getGlobalSetting: function (key, onSuccess, onFail) { diff --git a/src/sidebarApps/extensions/index.js b/src/sidebarApps/extensions/index.js index c59d9063c..07e14cc8a 100644 --- a/src/sidebarApps/extensions/index.js +++ b/src/sidebarApps/extensions/index.js @@ -11,6 +11,7 @@ import config from "lib/config"; import InstallState from "lib/installState"; import loadPlugin from "lib/loadPlugin"; import settings from "lib/settings"; +import { interstitialAd } from "lib/startAd"; import FileBrowser from "pages/fileBrowser"; import plugin from "pages/plugin"; import helpers from "utils/helpers"; @@ -861,10 +862,10 @@ function ListItem({ async function loadAd(el) { if (!helpers.canShowAds()) return; try { - if (!(await window.iad?.isLoaded())) { + if (!(await interstitialAd?.isLoaded())) { const oldText = el.textContent; el.textContent = strings["loading..."]; - await window.iad.load(); + await interstitialAd?.load(); el.textContent = oldText; } } catch (error) { diff --git a/src/sidebarApps/extensions/style.scss b/src/sidebarApps/extensions/style.scss index 18d79cfde..c123b8dc1 100644 --- a/src/sidebarApps/extensions/style.scss +++ b/src/sidebarApps/extensions/style.scss @@ -1,7 +1,33 @@ @use "../../styles/mixins.scss"; .container.extensions { + display: flex; + flex-direction: column; + overflow: hidden; + + .list.collapsible { + display: flex; + flex-direction: column; + overflow: hidden; + + &:not(.hidden) { + flex: 1; + min-height: 0; + } + + &.hidden { + flex-shrink: 0; + } + + >ul { + flex: 1; + min-height: 0; + overflow-y: auto; + } + } + .header { + flex-shrink: 0; padding: 10px; display: flex; flex-direction: column; @@ -67,7 +93,12 @@ } .list.search-result { - min-height: 0; + flex-shrink: 0; + overflow-y: auto; + + &:empty { + display: none + } &.loading { @include mixins.loader(20px); diff --git a/src/sidebarApps/notification/style.scss b/src/sidebarApps/notification/style.scss index 1ca2e0866..fa2a4db4b 100644 --- a/src/sidebarApps/notification/style.scss +++ b/src/sidebarApps/notification/style.scss @@ -154,6 +154,13 @@ } +@supports not (gap: 1px) { + .title > * + * { margin-left: 4px; } + .notifications-container > * + * { margin-top: 8px; } + .notification-item > * + * { margin-left: 10px; } + .action-button > * + * { margin-left: 4px; } +} + @keyframes slideIn { from { transform: translateX(100%); diff --git a/src/styles/codemirror.scss b/src/styles/codemirror.scss index 972b38cf9..f0321b160 100644 --- a/src/styles/codemirror.scss +++ b/src/styles/codemirror.scss @@ -92,6 +92,13 @@ overflow: auto; } +@supports not (gap: 1px) { + .cm-tooltip.cm-tooltip-autocomplete { + > * + * { margin-left: 0.4rem; } + > ul > li > * + * { margin-left: 0.12rem; } + } +} + @media (max-width: 480px) { .cm-tooltip { font-size: 0.9rem; diff --git a/src/styles/fileInfo.scss b/src/styles/fileInfo.scss index 9b3bc2397..c03f9343e 100644 --- a/src/styles/fileInfo.scss +++ b/src/styles/fileInfo.scss @@ -83,6 +83,12 @@ } } + @supports not (gap: 1px) { + .file-name > * + * { margin-left: 0.5rem; } + .info-grid > * { margin-right: 1.5rem; margin-bottom: 1.5rem; } + .info-item > * + * { margin-top: 0.5rem; } + } + @media (max-width: 480px) { .file-card { padding: 1.5rem; diff --git a/src/styles/wideScreen.scss b/src/styles/wideScreen.scss index ea12e7844..5d07be586 100644 --- a/src/styles/wideScreen.scss +++ b/src/styles/wideScreen.scss @@ -161,6 +161,15 @@ } #plugin { + @supports not (gap: 1px) { + .plugin-header > * { margin-right: 1.25rem; margin-bottom: 1.25rem; } + .plugin-header > *:last-child { margin-right: 0; margin-bottom: 0; } + .plugin-info > * + * { margin-left: 1.25rem; } + .plugin-title > * + * { margin-left: 1rem; } + .plugin-meta > * + * { margin-left: 0.75rem; } + .plugin-stats > * + * { margin-left: 0.5rem; } + } + .list-item { margin: 0 1rem 0.75rem 1rem; padding: 1.25rem; diff --git a/src/utils/helpers.js b/src/utils/helpers.js index ddea27fb4..d3175b225 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -4,6 +4,7 @@ import alert from "dialogs/alert"; import escapeStringRegexp from "escape-string-regexp"; import adRewards from "lib/adRewards"; import config from "lib/config"; +import { bannerAd, interstitialAd } from "lib/startAd"; import path from "./Path"; import Uri from "./Uri"; import Url from "./Url"; @@ -293,11 +294,11 @@ export default { async showInterstitialIfReady() { if (!this.canShowAds()) return false; if ( - typeof window.iad?.isLoaded === "function" && - typeof window.iad?.show === "function" && - (await window.iad.isLoaded()) + typeof interstitialAd?.isLoaded === "function" && + typeof interstitialAd?.show === "function" && + (await interstitialAd?.isLoaded()) ) { - window.iad.show(); + interstitialAd.show(); return true; } return false; @@ -306,17 +307,14 @@ export default { * Displays ad on the current page */ showAd() { - const { ad } = window; - if ( - this.canShowAds() && - innerHeight * devicePixelRatio > 600 && - typeof ad?.show === "function" - ) { - const $page = tag.getAll("wc-page:not(#root)").pop(); - if ($page) { - ad.active = true; - ad.show(); - } + if (!this.canShowAds()) return; + if (innerHeight * devicePixelRatio <= 600) return; + if (!bannerAd || typeof bannerAd.show !== "function") return; + + const $page = tag.getAll("wc-page:not(#root)").pop(); + if ($page) { + bannerAd.active = true; + bannerAd.show(); } }, async toInternalUri(uri) { diff --git a/www/index.html b/www/index.html index 7802e3a00..07f170909 100644 --- a/www/index.html +++ b/www/index.html @@ -1,195 +1,207 @@ - - - - - - + + + + window.addEventListener("error", handleStartupError); + window.addEventListener("unhandledrejection", handleStartupRejection); + })(); + - - + + - + - Acode - + Acode + - -
-
-
-
+ +
+
+
+
- + - - + }); + + + From 1318bbc3e978bd6c6f0a9ca198b2e4ffebdc0638 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Tue, 9 Jun 2026 19:06:16 +0530 Subject: [PATCH 2/3] fix: linting --- src/lib/devTools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/devTools.js b/src/lib/devTools.js index cc8d90789..b37bc75db 100644 --- a/src/lib/devTools.js +++ b/src/lib/devTools.js @@ -54,7 +54,7 @@ const devTools = { ); await fsOperation(DATA_STORAGE).createFile("eruda.js", erudaScript); } catch (e) { - console.error("Failed to download eruda.js", e); + console.error("Failed to download eruda.js", e); } finally { if (showLoader) loader.destroy(); } From 4f67df772b67617b07b87136752b7f36bf2ea08c Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Tue, 9 Jun 2026 19:34:36 +0530 Subject: [PATCH 3/3] fix: review changes --- src/handlers/keyboard.js | 4 ++-- src/lib/adRewards.js | 2 +- src/lib/devTools.js | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/handlers/keyboard.js b/src/handlers/keyboard.js index 9293d0f7e..145bc9dc5 100644 --- a/src/handlers/keyboard.js +++ b/src/handlers/keyboard.js @@ -116,17 +116,17 @@ document.addEventListener("deviceready", () => { softKeyboardHeight = keyboardHeight > MIN_KEYBOARD_HEIGHT ? keyboardHeight : 0; if (!externalKeyboard && softKeyboardHeight) { + toggleBannerAd(false); emit("keyboardShowStart"); } } else if (currentWindowHeight < window.innerHeight) { // height increasing if (!externalKeyboard && softKeyboardHeight) { + toggleBannerAd(true); emit("keyboardHideStart"); } } - toggleBannerAd(currentWindowHeight >= window.innerHeight); - currentWindowHeight = window.innerHeight; }); diff --git a/src/lib/adRewards.js b/src/lib/adRewards.js index 38a2450e0..3fc6aff68 100644 --- a/src/lib/adRewards.js +++ b/src/lib/adRewards.js @@ -89,7 +89,7 @@ function emitChange() { function hideActiveBanner() { if (bannerAd?.active) { bannerAd.active = false; - bannerAd?.hide(); + bannerAd.hide?.(); } } diff --git a/src/lib/devTools.js b/src/lib/devTools.js index b37bc75db..098e48c25 100644 --- a/src/lib/devTools.js +++ b/src/lib/devTools.js @@ -53,8 +53,7 @@ const devTools = { "utf-8", ); await fsOperation(DATA_STORAGE).createFile("eruda.js", erudaScript); - } catch (e) { - console.error("Failed to download eruda.js", e); + } catch { } finally { if (showLoader) loader.destroy(); }