From 95022e6f4d80e12beba8b5554b4f226df634894c Mon Sep 17 00:00:00 2001 From: Joseph Yaksich Date: Sun, 26 Jul 2026 18:56:45 +0000 Subject: [PATCH] fix: keep iOS content below system status area Signed-off-by: Joseph Yaksich --- CHANGELOG.md | 13 ++++++++++++- README.md | 2 +- capacitor.config.json | 2 +- ios/App/App.xcodeproj/project.pbxproj | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- public/index.html | 2 +- src/client/mobile.ts | 5 +++-- src/server/channel-computers.ts | 2 +- src/server/db.ts | 2 +- test/channel-computers.mjs | 2 +- test/mobile.mjs | 2 ++ 12 files changed, 30 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b106862..adda5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.15] - 2026-07-26 + +### Fixed + +- iPhone and iPad now reserve the iOS system status area for the Dynamic + Island, battery, and signal indicators instead of drawing the packaged + workspace underneath it. Full-screen headers such as Notes keep **New note** + and **Close** visible and tappable, while Android retains its existing + edge-to-edge behavior. + ## [0.0.14] - 2026-07-26 ### Changed @@ -396,7 +406,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 notarization, stapled tickets, Gatekeeper verification, persistent Application Support, and isolated Apple container machines. -[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.14...HEAD +[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.15...HEAD +[0.0.15]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.15 [0.0.14]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.14 [0.0.13]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.13 [0.0.12]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.12 diff --git a/README.md b/README.md index 4a34c3c..d5424f6 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ A fresh data directory opens first-run setup. The source runtime defaults to | `PORT` | `8123` | HTTP/WebSocket control-plane port. | | `CTRL_DATA_DIR` | `./data` | Databases, routing state, uploads, and narrow workspace mirrors. | | `HELM_CHANNEL_COMPUTER_BACKEND` | `apple` on macOS, `lxc` on Linux, `wsl` on Windows | Host isolation backend; `native` and `mock` are explicit development/test overrides. | -| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.14` | Versioned channel-machine image contract. | +| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.15` | Versioned channel-machine image contract. | ### Agent-first JSON CLI diff --git a/capacitor.config.json b/capacitor.config.json index 83bf98e..3d425d7 100644 --- a/capacitor.config.json +++ b/capacitor.config.json @@ -46,7 +46,7 @@ "layoutName": "launch_screen" }, "StatusBar": { - "overlaysWebView": true, + "overlaysWebView": false, "style": "DEFAULT", "backgroundColor": "#08090c" } diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 5f6dd6a..063e45e 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -300,14 +300,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 15; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.14; + MARKETING_VERSION = 0.0.15; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = com.gitcommit90.onehelm.mobile; PRODUCT_NAME = 1Helm; @@ -322,14 +322,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 15; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.14; + MARKETING_VERSION = 0.0.15; PRODUCT_BUNDLE_IDENTIFIER = com.gitcommit90.onehelm.mobile; PRODUCT_NAME = 1Helm; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; diff --git a/package-lock.json b/package-lock.json index 65e4408..d1b207b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "1helm", - "version": "0.0.14", + "version": "0.0.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "1helm", - "version": "0.0.14", + "version": "0.0.15", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 5d4ba5b..4f14500 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "1helm", "productName": "1Helm", - "version": "0.0.14", + "version": "0.0.15", "private": true, "type": "module", "license": "AGPL-3.0-only", diff --git a/public/index.html b/public/index.html index ae9a038..b7d5a91 100644 --- a/public/index.html +++ b/public/index.html @@ -34,6 +34,6 @@
- + diff --git a/src/client/mobile.ts b/src/client/mobile.ts index 711e9bb..a829aa7 100644 --- a/src/client/mobile.ts +++ b/src/client/mobile.ts @@ -83,7 +83,8 @@ export function serverWebSocketUrl(path: string): string { export async function initializeMobileRuntime(): Promise { if (!native) return localStorage.getItem("ctrl.token") || ""; - document.documentElement.dataset.nativeMobile = mobilePlatform(); + const platform = mobilePlatform(); + document.documentElement.dataset.nativeMobile = platform; serverOrigin = localStorage.getItem(SERVER_KEY) || ""; try { if (serverOrigin) serverOrigin = normalizeServerOrigin(serverOrigin); } catch { serverOrigin = ""; localStorage.removeItem(SERVER_KEY); } @@ -96,7 +97,7 @@ export async function initializeMobileRuntime(): Promise { await Promise.allSettled([ StatusBar.setStyle({ style: Style.Default }), - StatusBar.setOverlaysWebView({ overlay: true }), + StatusBar.setOverlaysWebView({ overlay: platform !== "ios" }), Keyboard.setResizeMode({ mode: KeyboardResize.Native }), ]); diff --git a/src/server/channel-computers.ts b/src/server/channel-computers.ts index 85dc3fb..5a05810 100644 --- a/src/server/channel-computers.ts +++ b/src/server/channel-computers.ts @@ -67,7 +67,7 @@ const APPLE_RUNTIME_VERSION = "1.1.0"; export const APPLE_RUNTIME_PACKAGE = `container-${APPLE_RUNTIME_VERSION}-installer-signed.pkg`; export const APPLE_RUNTIME_URL = `https://github.com/apple/container/releases/download/${APPLE_RUNTIME_VERSION}/${APPLE_RUNTIME_PACKAGE}`; export const APPLE_RUNTIME_SHA256 = "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714"; -export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.14"; +export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.15"; const CONTAINER_CANDIDATES = [process.env.HELM_CONTAINER_CLI, "/usr/local/bin/container", "/opt/homebrew/bin/container", "container"].filter(Boolean) as string[]; const LXC_RUNTIME_VERSION = "1helm-lxc-runtime-v1"; const LXC_HELPER_CANDIDATES = [ diff --git a/src/server/db.ts b/src/server/db.ts index 7335063..5f25f4e 100644 --- a/src/server/db.ts +++ b/src/server/db.ts @@ -979,7 +979,7 @@ export function migrate(): void { const platformBackend = process.platform === "darwin" ? "apple" : process.platform === "win32" ? "wsl" : "lxc"; const configuredBackend = String(process.env.HELM_CHANNEL_COMPUTER_BACKEND || platformBackend); const backend = ["apple", "lxc", "wsl", "native", "mock"].includes(configuredBackend) ? configuredBackend : platformBackend; - const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.14"); + const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.15"); // Earlier Linux/Windows releases persisted the compatibility `native` // seam into every channel row. A production host update must actually // move those rows onto the platform isolation backend; changing the unit's diff --git a/test/channel-computers.mjs b/test/channel-computers.mjs index be94c5c..40cb457 100644 --- a/test/channel-computers.mjs +++ b/test/channel-computers.mjs @@ -168,7 +168,7 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive test("runtime digest and packaged image recipe stay pinned", async () => { assert.equal(computers.APPLE_RUNTIME_SHA256, "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714"); assert.match(computers.APPLE_RUNTIME_URL, /\/1\.1\.0\/container-1\.1\.0-installer-signed\.pkg$/); - assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.14"); + assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.15"); const packaging = await readFile(join(root, "scripts", "package-mac-dmg.cjs"), "utf8"); assert.match(packaging, /container\(\?:\$\|\\\/\)/, "release packaging includes container/ image assets"); const image = await readFile(join(root, "container", "Containerfile"), "utf8"); diff --git a/test/mobile.mjs b/test/mobile.mjs index d05f6e6..ab1fb42 100644 --- a/test/mobile.mjs +++ b/test/mobile.mjs @@ -103,6 +103,7 @@ test("Capacitor shells keep sessions native, connections HTTPS-only, and release assert.equal(parsed.android.webContentsDebuggingEnabled, false); assert.equal(parsed.ios.preferredContentMode, "mobile"); assert.equal(parsed.ios.webContentsDebuggingEnabled, false); + assert.equal(parsed.plugins.StatusBar.overlaysWebView, false, "iOS reserves the system status area before the packaged UI paints"); assert.equal(parsed.plugins.SplashScreen.launchAutoHide, false, "native launch art remains only until the first real screen paints"); assert.ok(parsed.plugins.SplashScreen.launchShowDuration <= 500, "launch has no artificial logo hold"); assert.equal(parsed.plugins.SplashScreen.launchFadeOutDuration, 180); @@ -116,6 +117,7 @@ test("Capacitor shells keep sessions native, connections HTTPS-only, and release assert.match(mobile, /removeSecureSession/); assert.match(mobile, /SplashScreen\.hide\(\{ fadeOutDuration: 180 \}\)/); assert.match(mobile, /requestAnimationFrame\(\(\) => requestAnimationFrame/, "launch fades only after the gateway or workspace paints"); + assert.match(mobile, /StatusBar\.setOverlaysWebView\(\{ overlay: platform !== "ios" \}\)/, "iOS keeps every full-screen header below the Dynamic Island while Android retains edge-to-edge rendering"); assert.match(mobile, /App\.getLaunchUrl/, "a cold-start OAuth callback is retained"); assert.doesNotMatch(mobile, /destination\.origin === serverOrigin/, "server links cannot replace the audited packaged WebView"); assert.doesNotMatch(api, /let token = localStorage\.getItem/, "the session is not eagerly copied out of native secure storage");