From 821a649224531d34fd75b4b9cb4537add52101c2 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Feb 2024 03:48:14 +0800 Subject: [PATCH 1/6] fix: improve platform recognition then fix icon display in visionos --- src/ext/background/main.js | 14 +++++++++++++- xcode/Shared/Utilities.swift | 29 +++++++++++++++++++---------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index 2d90f256..1c51e2c4 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -72,9 +72,21 @@ function setClipboard(data, type = "text/plain") { } async function setBadgeCount() { - const clearBadge = () => browser.browserAction.setBadgeText({ text: "" }); + const clearBadge = () => { + if (import.meta.env.SAFARI_VERSION < 16.4) { + browser.browserAction.setBadgeText({ text: "" }); + } else { + browser.browserAction.setBadgeText({ text: null }); + } + }; // @todo until better introduce in ios, only set badge on macOS const platform = await getPlatform(); + // set a text badge or an empty string in visionOS will cause the extension's icon to no longer be displayed + // set it to null to fix already affected users + if (platform === "visionos") { + browser.browserAction.setBadgeText({ text: null }); + return; + } if (platform !== "macos") return clearBadge(); // @todo settingsStorage.get("global_exclude_match") const settings = await settingsStorage.get([ diff --git a/xcode/Shared/Utilities.swift b/xcode/Shared/Utilities.swift index c64c2fc6..8e3f0949 100644 --- a/xcode/Shared/Utilities.swift +++ b/xcode/Shared/Utilities.swift @@ -28,16 +28,25 @@ func directoryExists(path: String) -> Bool { } func getPlatform() -> String { - var platform:String -#if os(iOS) - if UIDevice.current.userInterfaceIdiom == .pad { - platform = "ipados" +#if os(macOS) + return "macos" +#elseif os(iOS) + // https://developer.apple.com/documentation/uikit/uiuserinterfaceidiom + switch UIDevice.current.userInterfaceIdiom { + case .phone: + return "ios" + case .pad, .mac: + return "ipados" + case .vision: + return "visionos" + case .tv: + return "tvos" + case .carPlay: + return "carplay" + case .unspecified: + return "unspecified" + @unknown default: + return "unknown" } - else { - platform = "ios" - } -#elseif os(macOS) - platform = "macos" #endif - return platform } From 54da240a7a1637f11468f2483e0623bdb9c8a2b5 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Feb 2024 03:50:53 +0800 Subject: [PATCH 2/6] refactor: solve the unpleasant bounce of loader icon --- src/ext/shared/Components/Loader.svelte | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ext/shared/Components/Loader.svelte b/src/ext/shared/Components/Loader.svelte index d35782e2..666ae404 100644 --- a/src/ext/shared/Components/Loader.svelte +++ b/src/ext/shared/Components/Loader.svelte @@ -5,12 +5,24 @@ export let abort = false; export let abortClick = () => {}; export let backgroundColor = "var(--color-bg-secondary)"; + + /** + * Avoid icon bouncing due to viewport height changes or banner insertion + * @type {import('svelte/action').Action} + */ + function lockIconTopPosition(node) { + const icon = node.querySelector("svg"); + const rect = icon.getBoundingClientRect(); + icon.style.top = rect.top.toString(); + icon.style.position = "fixed"; + }
{@html iconLoader} From b660a36d06e34e1e02234315795f63a63190326d Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:21:03 +0800 Subject: [PATCH 3/6] refactor: improve basic popup view structure resolve popup open size adaptation resolve height and safe area adaptation avoid some unpleasant height changes and bounces --- src/ext/action-popup/App.svelte | 286 ++++++++---------- src/ext/action-popup/Components/View.svelte | 10 +- .../Components/Views/InstallView.svelte | 5 - src/ext/action-popup/app.css | 54 ++-- src/ext/shared/utils.js | 1 + 5 files changed, 160 insertions(+), 196 deletions(-) diff --git a/src/ext/action-popup/App.svelte b/src/ext/action-popup/App.svelte index be597bf1..b3d36250 100644 --- a/src/ext/action-popup/App.svelte +++ b/src/ext/action-popup/App.svelte @@ -29,7 +29,6 @@ let platform; let initError; let firstGuide; - let windowHeight = 0; let header; let warn; let err; @@ -50,13 +49,10 @@ let installViewUserscriptError; let showAll; let allItems = []; - let resizeTimer; let abort = false; $: list = items.sort((a, b) => a.name.localeCompare(b.name)); - $: if (platform) document.body.classList.add(platform); - function getItemBackgroundColor(elements, index) { if (elements.length < 2) return null; if (elements.length % 2 === 0 && index % 2 === 0) return "light"; @@ -264,9 +260,6 @@ // set toggle state active = await settingsStorage.get("global_active"); - // set popup height - resize(); - // get matches const currentTab = await browser.tabs.getCurrent(); const url = currentTab.url; @@ -361,34 +354,15 @@ window.location.reload(); } - async function resize() { - if (!platform || platform === "macos") return; - clearTimeout(resizeTimer); - resizeTimer = setTimeout(async () => { - if (platform === "ipados") { - if (window.matchMedia("(max-width: 360px)").matches) { - // the popup window is no greater than 360px - // ensure body & main element have no leftover styling - main.removeAttribute("style"); - document.body.removeAttribute("style"); - return; - } - main.style.maxHeight = "unset"; - document.body.style.width = "100vw"; - } - // on ios and ipados (split view) programmatically set the height of the scrollable container - // first get the header height - const headerHeight = header.offsetHeight; - // then check if a warning or error is visible (ie. taking up height) - let addHeight = 0; - // if warn or error elements visible, also subtract that from applied height - if (warn) addHeight += warn.offsetHeight; - if (err) addHeight += err.offsetHeight; - windowHeight = window.outerHeight - (headerHeight + addHeight); - main.style.height = `${windowHeight}px`; - main.style.paddingBottom = `${headerHeight + addHeight}px`; - }, 25); - } + /** @type {import("svelte/elements").UIEventHandler} */ + // async function resizeHandler(event) { + // if (platform !== "macos") { + // const viewport = event.currentTarget.visualViewport; + // console.debug(platform, viewport); + // // add max limits after the window size has been rendered + // // avoid display overflow when window shrinks dynamically + // } + // } /** * Check if the current page contains a user script @@ -497,8 +471,6 @@ onMount(async () => { await initialize(); - // run resize again for good measure - resize(); }); // handle native app messages @@ -522,114 +494,6 @@ let showBetaNews = true; - -
- - (showUpdates = true)} - title={"Show updates"} - {disabled} - /> - - -
-{#if !active} - -{/if} -{#if showBetaNews && platform !== "macos"} -
- NEW: is - now available on iOS! - (showBetaNews = false)} - title={"Close"} - /> -
-{/if} -{#if showInstallPrompt} -
- Userscript - {#if scriptChecking} - {showInstallPrompt} - {:else} - {scriptInstalled ? "Installed" : "Detected"}: - - {/if} -
-{/if} -{#if errorNotification} -
- {errorNotification} - (errorNotification = undefined)} - title={"Clear error"} - /> -
-{/if} -
- {#if loading} - - {:else if inactive} -
Popup inactive on extension page
- {:else if firstGuide} -
-

Welcome, first use please: 

- -

to complete the initialization

-
- {:else if initError} -
- Something went wrong:  - -
- {:else if items.length < 1} -
No matched userscripts
- {:else} -
- {#each list as item, i (item.filename)} - toggleItem(item)} - /> - {/each} -
- {/if} -
-{#if !inactive && platform === "macos"} - -{/if} {#if showUpdates} +{:else} +
+
+ + (showUpdates = true)} + title={"Show updates"} + {disabled} + /> + + +
+ {#if !active} + + {/if} + {#if showBetaNews && platform !== "macos"} +
+ NEW: + is now available! + (showBetaNews = false)} + title={"Close"} + /> +
+ {/if} + {#if showInstallPrompt} +
+ Userscript + {#if scriptChecking} + {showInstallPrompt} + {:else} + {scriptInstalled ? "Installed" : "Detected"}: + + {/if} +
+ {/if} + {#if errorNotification} +
+ {errorNotification} + (errorNotification = undefined)} + title={"Clear error"} + /> +
+ {/if} +
+
+ {#if loading} + + {:else if inactive} +
Popup inactive on extension page
+ {:else if firstGuide} +
+

Welcome, first use please: 

+ +

to complete the initialization

+
+ {:else if initError} +
+ Something went wrong:  + +
+ {:else if items.length < 1} +
No matched userscripts
+ {:else} +
+ {#each list as item, i (item.filename)} + toggleItem(item)} + /> + {/each} +
+ {/if} +
+ {#if !inactive && platform === "macos"} + + {/if} {/if} diff --git a/src/ext/action-popup/Components/Views/InstallView.svelte b/src/ext/action-popup/Components/Views/InstallView.svelte index fe3a3152..d4d23d2b 100644 --- a/src/ext/action-popup/Components/Views/InstallView.svelte +++ b/src/ext/action-popup/Components/Views/InstallView.svelte @@ -76,11 +76,6 @@ padding: 0 1rem; } - :global(body.ios) .view--install, - :global(body.ipados) .view--install { - padding-bottom: calc(39px + 1rem); - } - .install__error { color: var(--text-color-disabled); font-weight: 600; diff --git a/src/ext/action-popup/app.css b/src/ext/action-popup/app.css index 2d2feca2..e0a88c6e 100644 --- a/src/ext/action-popup/app.css +++ b/src/ext/action-popup/app.css @@ -8,55 +8,45 @@ body { color: var(--text-color-primary); font: var(--text-medium); letter-spacing: var(--letter-spacing-medium); - position: relative; text-rendering: optimizelegibility; - width: 18rem; -webkit-font-smoothing: antialiased; + position: relative; + width: 18rem; + height: 26rem; } -/* attempts to detect iOS */ -@media (hover: none) { +/* ios */ +@supports (-webkit-touch-callout: none) { html { font-size: 125%; + overflow: visible; + overscroll-behavior: none; } body { - width: 100vw; + width: auto; + min-width: 16rem; + height: auto; } -} -@media screen and (width >= 481px) and (orientation: portrait) and (hover: none) { - body { - width: 18rem; + /* non-fixed width need to stretch */ + @media (width < 320px) { + body { + width: 18rem; + min-height: 16rem; + } } } -@media screen and (width >= 1024px) and (orientation: landscape) and (hover: none) { - body { - width: 18rem; - } -} - -body.ipados { - width: 18rem; - height: 100vh; -} - -body.ipados #app { - height: 100%; -} - -body.ios { - height: 100vh; - width: 100vw; +noscript { + display: block; } -body.ios #app { +#app { + display: flex; + flex-direction: column; height: 100%; -} - -noscript { - display: block; + justify-content: space-between; } button { diff --git a/src/ext/shared/utils.js b/src/ext/shared/utils.js index 96b76af3..63875afd 100644 --- a/src/ext/shared/utils.js +++ b/src/ext/shared/utils.js @@ -374,6 +374,7 @@ export async function openExtensionPage() { const tab = tabs.find((e) => e.url.startsWith(url)); if (!tab) return browser.tabs.create({ url }); await browser.tabs.update(tab.id, { active: true }); + if (!browser.windows.update) return; await browser.windows.update(tab.windowId, { focused: true }); } From af2954f17a66384945d4bd6e1429cd4e7d24236e Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:04:29 +0800 Subject: [PATCH 4/6] fix: think of visionos always as ios There is currently no effective way to detect visionOS in Apple Vision (Designed for iPad) mode. So it can only always be treated as ios to handle any surprises. setBadgeText `null` value tested working on Safari 15.6.1 (macOS 12.6). --- src/ext/background/main.js | 15 ++------------- xcode/Shared/Utilities.swift | 1 + 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index 1c51e2c4..135661bb 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -72,21 +72,10 @@ function setClipboard(data, type = "text/plain") { } async function setBadgeCount() { - const clearBadge = () => { - if (import.meta.env.SAFARI_VERSION < 16.4) { - browser.browserAction.setBadgeText({ text: "" }); - } else { - browser.browserAction.setBadgeText({ text: null }); - } - }; + const clearBadge = () => browser.browserAction.setBadgeText({ text: null }); // @todo until better introduce in ios, only set badge on macOS - const platform = await getPlatform(); // set a text badge or an empty string in visionOS will cause the extension's icon to no longer be displayed - // set it to null to fix already affected users - if (platform === "visionos") { - browser.browserAction.setBadgeText({ text: null }); - return; - } + const platform = await getPlatform(); if (platform !== "macos") return clearBadge(); // @todo settingsStorage.get("global_exclude_match") const settings = await settingsStorage.get([ diff --git a/xcode/Shared/Utilities.swift b/xcode/Shared/Utilities.swift index 8e3f0949..dfb00425 100644 --- a/xcode/Shared/Utilities.swift +++ b/xcode/Shared/Utilities.swift @@ -32,6 +32,7 @@ func getPlatform() -> String { return "macos" #elseif os(iOS) // https://developer.apple.com/documentation/uikit/uiuserinterfaceidiom + // There is no reliable way to detect visionOS for now, will match `.phone` in Apple Vision (Designed for iPad) mode switch UIDevice.current.userInterfaceIdiom { case .phone: return "ios" From 8d9b7a2a96ac06dcd46aa3975df400c27e2b0c55 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Mon, 12 Feb 2024 12:36:44 +0800 Subject: [PATCH 5/6] fix: revert view height and scrolling for macos --- src/ext/action-popup/Components/View.svelte | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/ext/action-popup/Components/View.svelte b/src/ext/action-popup/Components/View.svelte index 4a7564e7..805b5f16 100644 --- a/src/ext/action-popup/Components/View.svelte +++ b/src/ext/action-popup/Components/View.svelte @@ -40,11 +40,12 @@ background-color: var(--color-bg-secondary); display: flex; flex-direction: column; - left: 0; position: absolute; text-align: center; top: 0; + left: 0; width: 100%; + height: 100%; z-index: 3; } @@ -71,6 +72,18 @@ .view__body { flex-grow: 1; + overflow-y: auto; position: relative; } + + /* ios */ + @supports (-webkit-touch-callout: none) { + .view { + height: auto; + } + + .view__body { + overflow-y: visible; + } + } From 9d25dafcf58164bbd29e4a90e3cf36ffdf4aaaf3 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Mon, 12 Feb 2024 12:56:36 +0800 Subject: [PATCH 6/6] fix: make the subview none style consistent --- .../action-popup/Components/Views/AllItemsView.svelte | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ext/action-popup/Components/Views/AllItemsView.svelte b/src/ext/action-popup/Components/Views/AllItemsView.svelte index b5457123..2c5484bb 100644 --- a/src/ext/action-popup/Components/Views/AllItemsView.svelte +++ b/src/ext/action-popup/Components/Views/AllItemsView.svelte @@ -47,12 +47,10 @@ } .none { - align-items: center; - color: var(--text-color-disabled); - display: flex; font-weight: 600; - justify-content: center; - inset: 0; - position: absolute; + color: var(--text-color-disabled); + text-align: center; + padding: 3rem 0; + line-height: 2.5; }