mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-24 19:59:36 +00:00
Fix bird not flying to right location when iOS address bar is minimized
This commit is contained in:
62
dist/birb.js
vendored
62
dist/birb.js
vendored
@@ -187,6 +187,27 @@
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the inner browser window
|
||||
* Will be the same as getFixedWindowHeight() on most browsers
|
||||
* On iOS, it will vary to be the height excluding the current address bar size (potentially greater than fixed height)
|
||||
*/
|
||||
function getWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed/sticky elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fixed height of the inner browser window
|
||||
* Will be the same as getWindowHeight() on most browsers
|
||||
* On iOS, it will always be the height of the window when the address bar is fully expanded
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
function getFixedWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
}
|
||||
|
||||
/** Indicators for parts of the base bird sprite sheet */
|
||||
const Sprite = {
|
||||
THEME_HIGHLIGHT: "theme-highlight",
|
||||
@@ -752,7 +773,8 @@
|
||||
let bottom;
|
||||
if (this.isAbsolutePositioned) {
|
||||
// Position is absolute, convert from fixed
|
||||
bottom = y - window.scrollY;
|
||||
// Account for address bar shrinkage on iOS
|
||||
bottom = y - window.scrollY - (getWindowHeight() - getFixedWindowHeight());
|
||||
} else {
|
||||
// Position is fixed
|
||||
bottom = y;
|
||||
@@ -1652,7 +1674,7 @@
|
||||
insertModal(`${birdBirb()} Mode`, message);
|
||||
}),
|
||||
new Separator(),
|
||||
new MenuItem("2025.10.28.95", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.95"); }, false),
|
||||
new MenuItem("2025.10.28.152", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.152"); }, false),
|
||||
];
|
||||
|
||||
const styleElement = document.createElement("style");
|
||||
@@ -1968,7 +1990,7 @@
|
||||
targetY = getFocusedY();
|
||||
// Adjust startY to account for scrolling
|
||||
startY += targetY - oldTargetY;
|
||||
if (targetY < 0 || targetY > window.innerHeight) {
|
||||
if (targetY < 0 || targetY > getWindowHeight()) {
|
||||
// Fly to another element or the ground if the focused element moves out of bounds
|
||||
flySomewhere();
|
||||
}
|
||||
@@ -2098,8 +2120,8 @@
|
||||
return;
|
||||
}
|
||||
const y = parseInt(feather.style.top || "0") + FEATHER_FALL_SPEED;
|
||||
feather.style.top = `${Math.min(y, window.innerHeight - feather.offsetHeight)}px`;
|
||||
if (y < window.innerHeight - feather.offsetHeight) {
|
||||
feather.style.top = `${Math.min(y, getWindowHeight() - feather.offsetHeight)}px`;
|
||||
if (y < getWindowHeight() - feather.offsetHeight) {
|
||||
feather.style.left = `${Math.sin(3.14 * 2 * (ticks / 120)) * 25}px`;
|
||||
}
|
||||
}
|
||||
@@ -2109,7 +2131,7 @@
|
||||
*/
|
||||
function centerElement(element) {
|
||||
element.style.left = `${window.innerWidth / 2 - element.offsetWidth / 2}px`;
|
||||
element.style.top = `${window.innerHeight / 2 - element.offsetHeight / 2}px`;
|
||||
element.style.top = `${getWindowHeight() / 2 - element.offsetHeight / 2}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2141,7 +2163,7 @@
|
||||
// Right side
|
||||
x -= (menu.offsetWidth + offset) * UI_CSS_SCALE;
|
||||
}
|
||||
if (y > window.innerHeight / 2) {
|
||||
if (y > getWindowHeight() / 2) {
|
||||
// Top side
|
||||
y -= (menu.offsetHeight + offset + 10) * UI_CSS_SCALE;
|
||||
} else {
|
||||
@@ -2256,7 +2278,7 @@
|
||||
const dy = targetY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const time = Date.now() - stateStart;
|
||||
if (distance > Math.max(window.innerWidth, window.innerHeight) / 2) {
|
||||
if (distance > Math.max(window.innerWidth, getWindowHeight()) / 2) {
|
||||
speed *= 1.3;
|
||||
}
|
||||
const amount = Math.min(1, time / (distance / speed));
|
||||
@@ -2282,23 +2304,7 @@
|
||||
}
|
||||
|
||||
function getFocusedY() {
|
||||
return getFullWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The render-safe height of the inner browser window
|
||||
*/
|
||||
function getSafeWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
function getFullWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
return getWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2317,7 +2323,7 @@
|
||||
|
||||
function focusOnGround() {
|
||||
focusedElement = null;
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() };
|
||||
updateFocusedElementBounds();
|
||||
flyTo(Math.random() * window.innerWidth, 0);
|
||||
}
|
||||
|
||||
@@ -2333,7 +2339,7 @@
|
||||
const elements = document.querySelectorAll("img, video, .birb-sticky-note");
|
||||
const inWindow = Array.from(elements).filter((img) => {
|
||||
const rect = img.getBoundingClientRect();
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= window.innerHeight;
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= getWindowHeight();
|
||||
});
|
||||
/** @type {HTMLElement[]} */
|
||||
// @ts-expect-error
|
||||
@@ -2371,7 +2377,7 @@
|
||||
function updateFocusedElementBounds() {
|
||||
if (focusedElement === null) {
|
||||
// Update ground location to bottom of window
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() };
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getWindowHeight() };
|
||||
return;
|
||||
}
|
||||
let { left, right, top } = focusedElement.getBoundingClientRect();
|
||||
|
||||
64
dist/birb.user.js
vendored
64
dist/birb.user.js
vendored
@@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name Pocket Bird
|
||||
// @namespace https://idreesinc.com
|
||||
// @version 2025.10.28.95
|
||||
// @version 2025.10.28.152
|
||||
// @description birb
|
||||
// @author Idrees
|
||||
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
|
||||
@@ -201,6 +201,27 @@
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the inner browser window
|
||||
* Will be the same as getFixedWindowHeight() on most browsers
|
||||
* On iOS, it will vary to be the height excluding the current address bar size (potentially greater than fixed height)
|
||||
*/
|
||||
function getWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed/sticky elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fixed height of the inner browser window
|
||||
* Will be the same as getWindowHeight() on most browsers
|
||||
* On iOS, it will always be the height of the window when the address bar is fully expanded
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
function getFixedWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
}
|
||||
|
||||
/** Indicators for parts of the base bird sprite sheet */
|
||||
const Sprite = {
|
||||
THEME_HIGHLIGHT: "theme-highlight",
|
||||
@@ -766,7 +787,8 @@
|
||||
let bottom;
|
||||
if (this.isAbsolutePositioned) {
|
||||
// Position is absolute, convert from fixed
|
||||
bottom = y - window.scrollY;
|
||||
// Account for address bar shrinkage on iOS
|
||||
bottom = y - window.scrollY - (getWindowHeight() - getFixedWindowHeight());
|
||||
} else {
|
||||
// Position is fixed
|
||||
bottom = y;
|
||||
@@ -1666,7 +1688,7 @@
|
||||
insertModal(`${birdBirb()} Mode`, message);
|
||||
}),
|
||||
new Separator(),
|
||||
new MenuItem("2025.10.28.95", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.95"); }, false),
|
||||
new MenuItem("2025.10.28.152", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.152"); }, false),
|
||||
];
|
||||
|
||||
const styleElement = document.createElement("style");
|
||||
@@ -1982,7 +2004,7 @@
|
||||
targetY = getFocusedY();
|
||||
// Adjust startY to account for scrolling
|
||||
startY += targetY - oldTargetY;
|
||||
if (targetY < 0 || targetY > window.innerHeight) {
|
||||
if (targetY < 0 || targetY > getWindowHeight()) {
|
||||
// Fly to another element or the ground if the focused element moves out of bounds
|
||||
flySomewhere();
|
||||
}
|
||||
@@ -2112,8 +2134,8 @@
|
||||
return;
|
||||
}
|
||||
const y = parseInt(feather.style.top || "0") + FEATHER_FALL_SPEED;
|
||||
feather.style.top = `${Math.min(y, window.innerHeight - feather.offsetHeight)}px`;
|
||||
if (y < window.innerHeight - feather.offsetHeight) {
|
||||
feather.style.top = `${Math.min(y, getWindowHeight() - feather.offsetHeight)}px`;
|
||||
if (y < getWindowHeight() - feather.offsetHeight) {
|
||||
feather.style.left = `${Math.sin(3.14 * 2 * (ticks / 120)) * 25}px`;
|
||||
}
|
||||
}
|
||||
@@ -2123,7 +2145,7 @@
|
||||
*/
|
||||
function centerElement(element) {
|
||||
element.style.left = `${window.innerWidth / 2 - element.offsetWidth / 2}px`;
|
||||
element.style.top = `${window.innerHeight / 2 - element.offsetHeight / 2}px`;
|
||||
element.style.top = `${getWindowHeight() / 2 - element.offsetHeight / 2}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2155,7 +2177,7 @@
|
||||
// Right side
|
||||
x -= (menu.offsetWidth + offset) * UI_CSS_SCALE;
|
||||
}
|
||||
if (y > window.innerHeight / 2) {
|
||||
if (y > getWindowHeight() / 2) {
|
||||
// Top side
|
||||
y -= (menu.offsetHeight + offset + 10) * UI_CSS_SCALE;
|
||||
} else {
|
||||
@@ -2270,7 +2292,7 @@
|
||||
const dy = targetY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const time = Date.now() - stateStart;
|
||||
if (distance > Math.max(window.innerWidth, window.innerHeight) / 2) {
|
||||
if (distance > Math.max(window.innerWidth, getWindowHeight()) / 2) {
|
||||
speed *= 1.3;
|
||||
}
|
||||
const amount = Math.min(1, time / (distance / speed));
|
||||
@@ -2296,23 +2318,7 @@
|
||||
}
|
||||
|
||||
function getFocusedY() {
|
||||
return getFullWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The render-safe height of the inner browser window
|
||||
*/
|
||||
function getSafeWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
function getFullWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
return getWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2331,7 +2337,7 @@
|
||||
|
||||
function focusOnGround() {
|
||||
focusedElement = null;
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() };
|
||||
updateFocusedElementBounds();
|
||||
flyTo(Math.random() * window.innerWidth, 0);
|
||||
}
|
||||
|
||||
@@ -2347,7 +2353,7 @@
|
||||
const elements = document.querySelectorAll("img, video, .birb-sticky-note");
|
||||
const inWindow = Array.from(elements).filter((img) => {
|
||||
const rect = img.getBoundingClientRect();
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= window.innerHeight;
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= getWindowHeight();
|
||||
});
|
||||
/** @type {HTMLElement[]} */
|
||||
// @ts-expect-error
|
||||
@@ -2385,7 +2391,7 @@
|
||||
function updateFocusedElementBounds() {
|
||||
if (focusedElement === null) {
|
||||
// Update ground location to bottom of window
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() };
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getWindowHeight() };
|
||||
return;
|
||||
}
|
||||
let { left, right, top } = focusedElement.getBoundingClientRect();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Pocket Bird",
|
||||
"description": "It's a bird, in your browser. What more could you want?",
|
||||
"version": "2025.10.28.95",
|
||||
"version": "2025.10.28.152",
|
||||
"homepage_url": "https://idreesinc.com",
|
||||
"icons": {
|
||||
"48": "images/icons/transparent/48x48x1.png",
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
log,
|
||||
debug,
|
||||
error,
|
||||
getLayer
|
||||
getLayer,
|
||||
getWindowHeight
|
||||
} from './shared.js';
|
||||
import {
|
||||
Sprite,
|
||||
@@ -541,7 +542,7 @@ Promise.all([
|
||||
targetY = getFocusedY();
|
||||
// Adjust startY to account for scrolling
|
||||
startY += targetY - oldTargetY;
|
||||
if (targetY < 0 || targetY > window.innerHeight) {
|
||||
if (targetY < 0 || targetY > getWindowHeight()) {
|
||||
// Fly to another element or the ground if the focused element moves out of bounds
|
||||
flySomewhere();
|
||||
}
|
||||
@@ -674,8 +675,8 @@ Promise.all([
|
||||
return;
|
||||
}
|
||||
const y = parseInt(feather.style.top || "0") + FEATHER_FALL_SPEED;
|
||||
feather.style.top = `${Math.min(y, window.innerHeight - feather.offsetHeight)}px`;
|
||||
if (y < window.innerHeight - feather.offsetHeight) {
|
||||
feather.style.top = `${Math.min(y, getWindowHeight() - feather.offsetHeight)}px`;
|
||||
if (y < getWindowHeight() - feather.offsetHeight) {
|
||||
feather.style.left = `${Math.sin(3.14 * 2 * (ticks / 120)) * 25}px`;
|
||||
}
|
||||
}
|
||||
@@ -685,7 +686,7 @@ Promise.all([
|
||||
*/
|
||||
function centerElement(element) {
|
||||
element.style.left = `${window.innerWidth / 2 - element.offsetWidth / 2}px`;
|
||||
element.style.top = `${window.innerHeight / 2 - element.offsetHeight / 2}px`;
|
||||
element.style.top = `${getWindowHeight() / 2 - element.offsetHeight / 2}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -717,7 +718,7 @@ Promise.all([
|
||||
// Right side
|
||||
x -= (menu.offsetWidth + offset) * UI_CSS_SCALE;
|
||||
}
|
||||
if (y > window.innerHeight / 2) {
|
||||
if (y > getWindowHeight() / 2) {
|
||||
// Top side
|
||||
y -= (menu.offsetHeight + offset + 10) * UI_CSS_SCALE;
|
||||
} else {
|
||||
@@ -833,7 +834,7 @@ Promise.all([
|
||||
const dy = targetY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const time = Date.now() - stateStart;
|
||||
if (distance > Math.max(window.innerWidth, window.innerHeight) / 2) {
|
||||
if (distance > Math.max(window.innerWidth, getWindowHeight()) / 2) {
|
||||
speed *= 1.3;
|
||||
}
|
||||
const amount = Math.min(1, time / (distance / speed));
|
||||
@@ -859,23 +860,7 @@ Promise.all([
|
||||
}
|
||||
|
||||
function getFocusedY() {
|
||||
return getFullWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The render-safe height of the inner browser window
|
||||
*/
|
||||
function getSafeWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
function getFullWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
return getWindowHeight() - focusedBounds.top;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -894,7 +879,7 @@ Promise.all([
|
||||
|
||||
function focusOnGround() {
|
||||
focusedElement = null;
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() };
|
||||
updateFocusedElementBounds();
|
||||
flyTo(Math.random() * window.innerWidth, 0);
|
||||
}
|
||||
|
||||
@@ -910,7 +895,7 @@ Promise.all([
|
||||
const elements = document.querySelectorAll("img, video, .birb-sticky-note");
|
||||
const inWindow = Array.from(elements).filter((img) => {
|
||||
const rect = img.getBoundingClientRect();
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= window.innerHeight;
|
||||
return rect.left >= 0 && rect.top >= MIN_FOCUS_ELEMENT_TOP && rect.right <= window.innerWidth && rect.top <= getWindowHeight();
|
||||
});
|
||||
/** @type {HTMLElement[]} */
|
||||
// @ts-expect-error
|
||||
@@ -948,7 +933,7 @@ Promise.all([
|
||||
function updateFocusedElementBounds() {
|
||||
if (focusedElement === null) {
|
||||
// Update ground location to bottom of window
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() };
|
||||
focusedBounds = { left: 0, right: window.innerWidth, top: getWindowHeight() };
|
||||
return;
|
||||
}
|
||||
let { left, right, top } = focusedElement.getBoundingClientRect();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Directions, getLayer } from './shared.js';
|
||||
import { Directions, getLayer, getWindowHeight, getFixedWindowHeight } from './shared.js';
|
||||
import Layer from './layer.js';
|
||||
import Frame from './frame.js';
|
||||
import Anim from './anim.js';
|
||||
@@ -201,7 +201,8 @@ export class Birb {
|
||||
let bottom;
|
||||
if (this.isAbsolutePositioned) {
|
||||
// Position is absolute, convert from fixed
|
||||
bottom = y - window.scrollY;
|
||||
// Account for address bar shrinkage on iOS
|
||||
bottom = y - window.scrollY - (getWindowHeight() - getFixedWindowHeight());
|
||||
} else {
|
||||
// Position is fixed
|
||||
bottom = y;
|
||||
|
||||
@@ -182,4 +182,25 @@ export function getLayer(spriteSheet, spriteIndex, width) {
|
||||
layer.push(spriteSheet[y].slice(spriteIndex * width, (spriteIndex + 1) * width));
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the inner browser window
|
||||
* Will be the same as getFixedWindowHeight() on most browsers
|
||||
* On iOS, it will vary to be the height excluding the current address bar size (potentially greater than fixed height)
|
||||
*/
|
||||
export function getWindowHeight() {
|
||||
// Necessary because iOS 26 Safari is terrible and won't render
|
||||
// fixed/sticky elements behind the address bar
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fixed height of the inner browser window
|
||||
* Will be the same as getWindowHeight() on most browsers
|
||||
* On iOS, it will always be the height of the window when the address bar is fully expanded
|
||||
* @returns The true height of the inner browser window
|
||||
*/
|
||||
export function getFixedWindowHeight() {
|
||||
return document.documentElement.clientHeight;
|
||||
}
|
||||
Reference in New Issue
Block a user