Skip to content

feat: publish native overlay hosts as an independent package - #137

Open
huhuanming wants to merge 6 commits into
mainfrom
codex/native-overlay-migration
Open

huhuanming wants to merge 6 commits into
mainfrom
codex/native-overlay-migration

Conversation

@huhuanming

@huhuanming huhuanming commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Move the overlay hosts into a publishable native-view package so app-monorepo consumes a registry version instead of an in-repo workspace copy. This migrates the Android, iOS, custom C++, TypeScript and Web implementation; the SPEC explains the Fabric/custom-shadow-node requirement. The existing native-sheet API remains available.

At 59b3f142, the three confirmed review findings are fixed: late Web page portals inherit suspended-owner isolation; blocking isolation compares overlay level before sequence, so a newer modal remains inert under an older secure entry; Android page overlays isolate the page/navigation accessibility tree and restore the caller's prior flags on dismissal, suspension, detach and blocker transitions. These corrections keep the existing public contract. The Android CI now executes the overlay unit suite along with the nine existing suites.

All 42 publishable workspaces and internal exact dependencies move together to fresh 3.0.162-alpha.272. The original manual package-publish workflow is dispatched on this branch with npm_dist_tag=next and an empty only_workspace. The complete branch publication succeeded. Independent verification confirms all 42 exact versions, next tags and downloadable tarballs (HTTP200), all 42 latest tags unchanged, and package gitHead 59b3f142d6e4b9d25cfdd359574c2947700dd559. App-monorepo integration waits for final native-tests completion. The previous unified alpha270 release succeeded at run #271. No already-published version is overwritten or deleted; no merge is requested.

Validation for the correction:

  • Web baseline reproduced all three new regression failures; after the fixes, 4 Jest suites / 42 tests pass, including the three DOM regressions.
  • Android Kotlin compilation and 6 Robolectric tests pass (0 failures/errors/skips), covering actual accessibility flags and their restoration.
  • Typecheck, Builder Bob, lint (0 errors, 11 existing inline-style warnings), tarball contents and immutable Yarn installation pass.
  • 36 release-tool tests, 63 test-integrity tool tests and the repository integrity check pass.
  • The first native-tests run passed the package, ten Android suites, Kotlin CLI and SwiftPM jobs; iOS stopped before compilation because the example Podfile.lock still used alpha270. Commit 14847dd339234a7fd85d4fa523360b777f7fcc2e updates only that lockfile, generated with pinned CocoaPods1.16.2. The new native-tests run succeeded: package validation, all ten Android suites, Kotlin CLI, all three SwiftPM suites, lockfile verification, explicit NativeOverlay iOS Simulator compilation and all six XCTest suites passed. Package implementation and release contents are unchanged by the lockfile repair. The previous implementation's native-tests passed all nine Android suites, Kotlin CLI, three SwiftPM suites, explicit iOS Simulator overlay pod compilation and six XCTest suites.
  • Interactive iOS, real devices/iPad, real TalkBack/VoiceOver navigation and password-lock acceptance remain outstanding. Android flag tests do not claim screen-reader end-to-end acceptance.

App-monorepo integration and UI verification are tracked separately. Native main startup imports are reduced while preserving the upstream Tamagui factory and development validation; the existing budget threshold is unchanged.

Migrate the verified Fabric Android, iOS, C++ and Web implementation from
app-monorepo with a documented Nitro-template exception and standalone build,
type, test and packaging checks. Preserve the native-sheet compatibility API.

Align 42 release workspaces to 3.0.162-alpha.268 for branch CI publication on
next. Extend dependency synchronization to kit/playground and the temporary
overlay workspace reference; keep stable tags and PR merges unchanged.

Validation: 39 overlay tests, 36 release-tool tests, typecheck, lint, test
integrity, immutable install, JS/declaration/tarball build, full Android
arm64-v8a example build, and CocoaPods install passed. iOS runtime remains
unverified on this host.
@socket-security

socket-security Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​jest-environment-jsdom@​29.7.01001006593100
Addednpm/​@​types/​react-dom@​19.2.31001007592100

View full report

@huhuanming
huhuanming marked this pull request as ready for review October 1, 2026 09:25
const element = ref.current;
if (!element) {
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: [Hide page entries mounted after their owner is suspended]

When a page owner is already suspended and another overlay from that owner mounts, suspendedOwners remains unchanged. This effect does not run for the new portal child, leaving that entry visible and interactive over the active page.

Apply the current suspension state when page entries are inserted, as well as when the owner list changes.

container
.querySelectorAll<HTMLElement>(`[${ENTRY_ATTRIBUTE}]`)
.forEach((node) => {
setInert(node, Number(node.dataset.stackOrder) < seq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: [Use level order when inerting page overlays]

If a blocking secure page overlay opens before a modal page overlay, the later modal has a higher sequence number but still renders below secure. This comparison leaves the modal focusable and accessible beneath the blocking overlay.

Compare page entries using level order first and sequence within a level when applying inert.

private fun updateAccessibility() {
val entries = orderedEntries()
val topIndex = entries.indexOfLast { it.blocking && it.isShownForInput }
val content = parent as? ViewGroup

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: [Apply Android accessibility isolation to page overlays]

When a blocking page overlay is attached, it lives in NativeOverlayPageHostView, while this accessibility pass checks only the global orderedEntries(). Page attachment also does not trigger this pass, so TalkBack can still navigate underlying page controls.

Update accessibility state when page entries attach or detach, and hide page content outside the active overlay without hiding the page host.

* predictive back, so the dispatcher alone never runs first.
*/
fun handleBackKey(event: KeyEvent): Boolean {
val entry = topBlockingEntry() ?: return false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: [Handle Back for dismissible non-blocking overlays on Android]

When a non-blocking overlay is explicitly dismissible, topBlockingEntry() returns no entry (or a blocking entry beneath it). Back then reaches navigation or dismisses the lower overlay instead of the visible one. Please select the topmost input-ready entry that is either blocking or dismissible, then consume Back according to its dismissal setting.

const ownerKey = props.ownerKey ?? pageScope.ownerKey;
// Without a host or owner there is nowhere to anchor a page overlay.
const scope =
props.scope === 'page' && hostKey && ownerKey ? 'page' : 'global';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: [Keep page-scoped overlays within their page boundary]

If a caller requests page scope before hostKey or ownerKey is available, this expression changes the entry to global scope. The overlay can then appear above unrelated routes and will not suspend with its owning page. Please keep it page-scoped and withhold presentation until its required host and owner are available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants