Skip to content

fix(ios): land Databricks workspaces on /omnigent and hide the workspace chrome - #4559

Merged
fanzeyi merged 1 commit into
omnigent-ai:mainfrom
fanzeyi:fanzeyi/ios-workspace-chrome-and-mount
Aug 11, 2026
Merged

fix(ios): land Databricks workspaces on /omnigent and hide the workspace chrome#4559
fanzeyi merged 1 commit into
omnigent-ai:mainfrom
fanzeyi:fanzeyi/ios-workspace-chrome-and-mount

Conversation

@fanzeyi

@fanzeyi fanzeyi commented Aug 10, 2026

Copy link
Copy Markdown
Member

Related issue

N/A — no tracking issue; requested directly.

Summary

  • A workspace-hosted Omnigent on iOS was unusable in two ways: connecting with a
    bare workspace URL landed on the Databricks landing page instead of the app,
    and once on the app the workspace's top-nav bar was still painted over it —
    wasting vertical space and letting the user navigate into another workspace app
    with no way back.
  • Ports the desktop's chrome hide (web/electron/src/workspace-chrome.js) into a
    testable WorkspaceChromeScript, replacing the previous injection that was
    gated on path.starts(with: "/ml/omnigents") — a path gate skips auth-redirect
    landings and the /omnigent mount entirely. Keyed on the pinned origin, never
    on the path.
  • Mirrors the Android /omnigent bounce from feat(android): land Databricks workspaces on the /omnigent mount #4543 on iOS: domain-matched with no
    probe, ?o=<org> and fragment preserved, one bounce per app-page load, wired
    into the three iOS equivalents of Android's callbacks — decidePolicyFor
    (link/redirect navs), didCommit (every committed load, incl. the login
    chain's POST hand-back) and KVO on webView.url (in-page pushState, which
    fires no navigation callback at all).
  • Root cause both of the above depended on: with allowsInsecureHTTP (debug
    only), a schemeless host was normalized to http://, so the app pinned
    http:// while the server redirects to https://. Every pinned-origin
    comparison then failed silently — the chrome overlay, native bridge trust
    (isTrustedBridgeMessage, so the server switcher / Chat-Terminal bar / sidebar
    drag were dead), media-capture prompts, and load-success recording. A schemeless
    host now defaults to https unless it is loopback, mirroring the desktop's
    LOCAL_HOSTS, so the mismatch can't be created: release builds already reject
    http:// outright and App Transport Security blocks it at the network layer.
  • Derives the pinned origin from the pinned URL instead of caching it in a second
    field, so the two can't drift, and re-arms the bare-root bounce budget when a
    new server is pinned. Drops loadSucceeded's URL argument: its only consumer
    discarded it.

ELI5: the app was told "the server is http://host", the server answered
"actually I'm https://host", and every later "is this page still my server?"
check compared the two strings, said no, and quietly skipped its work.

  connect "dbc-x.cloud.databricks.com"
        │
        ├─ before: http://dbc-x…            → pinned http://dbc-x
        │          server 301 → https://…   → page   https://dbc-x
        │          pinned != page  ─────────► chrome hide / bridge / recents SKIPPED
        │
        └─ after:  https://dbc-x…           → pinned https://dbc-x  (non-loopback ⇒ https)
                   bare root ⇒ /omnigent    → bounce once
                   pinned == page  ─────────► overlay covers the workspace bar

Test Plan

  • cd web/ios && xcodebuild test -scheme Omnigent -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -only-testing:OmnigentTests → all tests pass.
  • New/updated unit tests: WorkspaceChromeScriptTests (CSS byte-identical to the
    desktop's WORKSPACE_CHROME_HIDE_CSS, the install-once guard, CSS embedded as
    an escaped literal); WorkspaceMountURLTests (bare roots on both workspace
    domains, query + fragment preserved, port and host-case, non-root paths left
    alone, databricksapps.com and a databricks.com.evil.example lookalike
    rejected, non-http schemes rejected); ServerURLTests (schemeless host → https
    even under the debug policy, loopback → http, explicit http:// honoured).
  • Manual, iPhone 17 Pro simulator against a real Databricks workspace: a bare
    workspace URL lands on /omnigent and the workspace nav bar is gone. Confirmed
    during development with a temporary in-app probe (since removed) reporting
    styleTag:true, position:"fixed", rect:{y:0,h:874} — the embed root covers the
    viewport from y=0 — and independently by the maintainer on the same simulator.
  • pre-commit run --files <touched files> clean.

Demo

Before:
ios-workspace-chrome-BEFORE

After:

ios-workspace-chrome-AFTER

Type of change

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

Coverage notes

The mount-URL rewriting, scheme defaulting and the injected script are
unit-tested. The navigation wiring is not: OmnigentWebView.Coordinator needs a
live WKWebView plus a SwiftUI context to construct, so the callbacks and the
overlay were verified on the simulator against a real workspace instead.

Changelog

Connecting the iOS app to a Databricks workspace now opens Omnigent directly and
hides the workspace navigation bar

Signed-off-by: Zeyi (Rice) Fan zeyi.f@databricks.com

@github-actions github-actions Bot added the size/L Pull request size: L label Aug 10, 2026
@fanzeyi
fanzeyi force-pushed the fanzeyi/ios-workspace-chrome-and-mount branch from f87bd65 to 191da78 Compare August 10, 2026 23:50
@github-actions github-actions Bot added size/XL Pull request size: XL and removed size/L Pull request size: L labels Aug 10, 2026
@fanzeyi
fanzeyi force-pushed the fanzeyi/ios-workspace-chrome-and-mount branch from 191da78 to 02bd815 Compare August 11, 2026 00:06
@github-actions github-actions Bot added size/L Pull request size: L and removed size/XL Pull request size: XL labels Aug 11, 2026
…ace chrome

## Related issue

N/A — no tracking issue; requested directly.

## Summary

- A workspace-hosted Omnigent on iOS was unusable in two ways: connecting with a
  bare workspace URL landed on the Databricks landing page instead of the app,
  and once on the app the workspace's top-nav bar was still painted over it —
  wasting vertical space and letting the user navigate into another workspace app
  with no way back.
- Ports the desktop's chrome hide (`web/electron/src/workspace-chrome.js`) into a
  testable `WorkspaceChromeScript`, replacing the previous injection that was
  gated on `path.starts(with: "/ml/omnigents")` — a path gate skips auth-redirect
  landings and the `/omnigent` mount entirely. Keyed on the pinned origin, never
  on the path.
- Mirrors the Android `/omnigent` bounce from omnigent-ai#4543 on iOS: domain-matched with no
  probe, `?o=<org>` and fragment preserved, one bounce per app-page load, wired
  into the three iOS equivalents of Android's callbacks — `decidePolicyFor`
  (link/redirect navs), `didCommit` (every committed load, incl. the login
  chain's POST hand-back) and KVO on `webView.url` (in-page `pushState`, which
  fires no navigation callback at all).
- Root cause both of the above depended on: with `allowsInsecureHTTP` (debug
  only), a schemeless host was normalized to `http://`, so the app pinned
  `http://` while the server redirects to `https://`. Every pinned-origin
  comparison then failed silently — the chrome overlay, native bridge trust
  (`isTrustedBridgeMessage`, so the server switcher / Chat-Terminal bar / sidebar
  drag were dead), media-capture prompts, and load-success recording. A schemeless
  host now defaults to https unless it is loopback, mirroring the desktop's
  `LOCAL_HOSTS`, so the mismatch can't be created: release builds already reject
  `http://` outright and App Transport Security blocks it at the network layer.
- Derives the pinned origin from the pinned URL instead of caching it in a second
  field, so the two can't drift, and re-arms the bare-root bounce budget when a
  new server is pinned. Drops `loadSucceeded`'s URL argument: its only consumer
  discarded it.

ELI5: the app was told "the server is http://host", the server answered
"actually I'm https://host", and every later "is this page still my server?"
check compared the two strings, said no, and quietly skipped its work.

```
  connect "dbc-x.cloud.databricks.com"
        │
        ├─ before: http://dbc-x…            → pinned http://dbc-x
        │          server 301 → https://…   → page   https://dbc-x
        │          pinned != page  ─────────► chrome hide / bridge / recents SKIPPED
        │
        └─ after:  https://dbc-x…           → pinned https://dbc-x  (non-loopback ⇒ https)
                   bare root ⇒ /omnigent    → bounce once
                   pinned == page  ─────────► overlay covers the workspace bar
```

## Test Plan

- `cd web/ios && xcodebuild test -scheme Omnigent -destination 'platform=iOS
  Simulator,name=iPhone 17 Pro' -only-testing:OmnigentTests` → all tests pass.
- New/updated unit tests: `WorkspaceChromeScriptTests` (CSS byte-identical to the
  desktop's `WORKSPACE_CHROME_HIDE_CSS`, the install-once guard, CSS embedded as
  an escaped literal); `WorkspaceMountURLTests` (bare roots on both workspace
  domains, query + fragment preserved, port and host-case, non-root paths left
  alone, `databricksapps.com` and a `databricks.com.evil.example` lookalike
  rejected, non-http schemes rejected); `ServerURLTests` (schemeless host → https
  even under the debug policy, loopback → http, explicit `http://` honoured).
- Manual, iPhone 17 Pro simulator against a real Databricks workspace: a bare
  workspace URL lands on `/omnigent` and the workspace nav bar is gone. Confirmed
  during development with a temporary in-app probe (since removed) reporting
  `styleTag:true, position:"fixed", rect:{y:0,h:874}` — the embed root covers the
  viewport from y=0 — and independently by the maintainer on the same simulator.
- `pre-commit run --files <touched files>` clean.

## Demo

Before / after on the iPhone 17 Pro simulator against a workspace-hosted server:
the Databricks top-nav bar (logo, workspace switcher, app switcher, avatar) is
painted above the app before, and the app fills the viewport after. Screenshots
attached below.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] UI / frontend change
- [ ] Refactor / chore
- [ ] Docs
- [ ] Test / CI
- [ ] Breaking change

## Test coverage

- [x] Unit tests added / updated
- [ ] Integration tests added / updated
- [ ] E2E tests added / updated
- [x] Manual verification completed
- [ ] Existing tests cover this change
- [ ] Not applicable

## Coverage notes

The mount-URL rewriting, scheme defaulting and the injected script are
unit-tested. The navigation wiring is not: `OmnigentWebView.Coordinator` needs a
live `WKWebView` plus a SwiftUI context to construct, so the callbacks and the
overlay were verified on the simulator against a real workspace instead.

## Changelog

Connecting the iOS app to a Databricks workspace now opens Omnigent directly and
hides the workspace navigation bar

Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
@fanzeyi
fanzeyi force-pushed the fanzeyi/ios-workspace-chrome-and-mount branch from 02bd815 to 045910c Compare August 11, 2026 00:14
@fanzeyi
fanzeyi enabled auto-merge (squash) August 11, 2026 00:17
@fanzeyi
fanzeyi merged commit 2cf72dd into omnigent-ai:main Aug 11, 2026
33 checks passed
@github-actions github-actions Bot added the no-doc-update Merged PR does not need a docs update label Aug 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ Doc impact: no-doc-update

iOS/Electron shell changes to workspace-root bounce handling, chrome-hiding CSS refactor, and scheme defaults are internal client behavior with no change to documented setup, configuration, or user-facing surfaces.

Auto-classified on merge. Set the label manually before merging to override. · run

github-actions Bot pushed a commit that referenced this pull request Aug 11, 2026
…e viewport (#4568)

## Related issue

N/A — reported directly after #4559.

## Summary

- On a Databricks workspace-hosted server, the iOS app renders its top controls
  under the status bar / Dynamic Island: the sidebar toggle sits level with the
  clock and the chat header is flush at y=0. Self-hosted (OSS) servers are fine,
  and Android is fine.
- All of the shell's iOS insets derive from `env(safe-area-inset-*)`, which is
  non-zero only when the document's meta viewport carries `viewport-fit=cover`.
  No document ships it, so the bridge script installs it at `.atDocumentStart`.
- The workspace host then reassigns the whole `content` attribute once its app
  mounts (`useMobileViewport`, called for the Omnigent route), writing
  `width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no` —
  no `viewport-fit`. `env()` collapses to 0, so `--omnigent-safe-top` and
  `--omnigent-inset-top` become 0 and every rule padding for the notch pads by
  nothing.
- Re-asserts the token with a `MutationObserver` on `document.head` instead of
  trusting the one-shot injection: the host rewrites again on its own re-renders,
  and it may replace the tag rather than edit it. The observer only writes when
  `viewport-fit=cover` is absent, so the shell's own write settles instead of
  looping.
- Latent until now — the workspace nav bar used to occupy the top of the screen
  and pushed the app below the unsafe area. #4559 promotes the app to a
  full-viewport overlay to hide that bar, which is what exposes the missing inset.
- Android is unaffected and untouched: it injects measured insets as
  `--omnigent-android-safe-area-*` (`MainActivity.kt`) and never depends on
  `env()`. Only iOS trusts the page's viewport metadata.

```
  documentStart : … user-scalable=no, viewport-fit=cover   ← shell installs it
  host mounts   : … user-scalable=no                       ← token dropped
                  env(safe-area-inset-top) = 0px  →  header y = 0   (under the island)
  observer      : … user-scalable=no, viewport-fit=cover   ← re-asserted
                  env(safe-area-inset-top) = 62px →  header y = 54  (clear)
```

## Test Plan

- `cd web/ios && xcodebuild test -scheme Omnigent -destination 'platform=iOS
  Simulator,name=iPhone 17 Pro' -only-testing:OmnigentTests` → all tests pass.
- Manual, iPhone 17 Pro simulator against a real Databricks workspace, measuring
  from inside the page (temporary probe, since removed) at three points — page
  load, after the host's app mounts, and after further re-renders:
  - before this change, once the host mounted: `viewport-fit` gone,
    `env(safe-area-inset-top)` `0px`, `--omnigent-inset-top` `max(0px, 0px)`,
    `.chat-header` at `y=0`.
  - after: `viewport-fit=cover` present at all three points,
    `env(safe-area-inset-top)` `62px`, `--omnigent-inset-top` `max(62px, 0px)`,
    `.chat-header` at `y=54`, stable across re-renders.
  - to confirm the diagnosis before fixing, re-adding the token by hand at
    runtime moved the header from `y=0` to `y=54` on its own.
- `pre-commit run --files web/ios/Omnigent/OmnigentWebView.swift` clean.

## Demo

Workspace-hosted server on the iPhone 17 Pro simulator. Before: the sidebar
toggle renders level with the status bar clock. After: it clears the status bar.
Screenshots attached below.

## Type of change

- [x] Bug fix
- [ ] Feature
- [x] UI / frontend change
- [ ] Refactor / chore
- [ ] Docs
- [ ] Test / CI
- [ ] Breaking change

## Test coverage

- [ ] Unit tests added / updated
- [ ] Integration tests added / updated
- [ ] E2E tests added / updated
- [x] Manual verification completed
- [ ] Existing tests cover this change
- [ ] Not applicable

## Coverage notes

Not unit-tested: the fix is JavaScript embedded in a Swift string literal and
injected into a live `WKWebView`, and the behaviour it guards against only happens
when a third-party host page mutates the DOM after mount — there's no harness that
reproduces that. Verified by measuring the computed inset and header position in
the page against a real workspace, before and after, including after subsequent
host re-renders. A follow-up worth doing: push measured safe-area insets from
native as Android does, so iOS stops depending on page viewport metadata at all.

## Changelog

Fixed iOS controls rendering under the status bar on Databricks workspace-hosted
servers

Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
fanzeyi added a commit that referenced this pull request Aug 11, 2026
## Related issue

N/A — release chore.

## Summary

- Bumps `MARKETING_VERSION` from 0.1.1 to 0.1.2 for the `ai.omnigent.ios` target
  (Debug and Release) ahead of a TestFlight build, so testers can tell the build
  carrying the workspace fixes apart from earlier 0.1.1 uploads.
- Covers two user-facing iOS fixes now on main: connecting to a Databricks
  workspace opens Omnigent directly and hides the workspace nav bar (#4559), and
  the top controls no longer render under the status bar on workspace-hosted
  servers (#4568).
- Only the app target moves. The `.tests` / `.uitests` bundles stay at 0.1.0 —
  they are never shipped, and `web/ios/RELEASE.md` scopes manual bumps to the
  Omnigent target.
- `CURRENT_PROJECT_VERSION` is deliberately untouched: the `beta` lane computes
  the build number as `latest_testflight_build_number + 1` and injects it via an
  xcodebuild override, so bumping it in git would only add churn.
- Not part of the repo-wide version lockstep: `scripts/update_versions.py` covers
  the Python packages and the Electron desktop app, not the iOS project.

## Test Plan

- `xcodebuild -project web/ios/Omnigent.xcodeproj -target Omnigent
  -showBuildSettings -configuration Release` reports `MARKETING_VERSION = 0.1.2`
  and `PRODUCT_BUNDLE_IDENTIFIER = ai.omnigent.ios`, confirming the resolved
  setting rather than just the edited text.
- `python scripts/update_versions.py check` is unaffected (iOS is not one of the
  locked locations).
- `pre-commit run --files web/ios/Omnigent.xcodeproj/project.pbxproj` clean.

## Demo

N/A — version metadata only, no UI change.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] UI / frontend change
- [x] Refactor / chore
- [ ] Docs
- [ ] Test / CI
- [ ] Breaking change

## Test coverage

- [ ] Unit tests added / updated
- [ ] Integration tests added / updated
- [ ] E2E tests added / updated
- [x] Manual verification completed
- [ ] Existing tests cover this change
- [x] Not applicable

## Coverage notes

Build-setting metadata with no runtime behaviour, so there is nothing to unit
test. Verified by reading back the resolved `MARKETING_VERSION` from
`xcodebuild -showBuildSettings` for the Release configuration of the shipping
target.

## Changelog

N/A

Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-doc-update Merged PR does not need a docs update size/L Pull request size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant