Repository navigation
feat(mobile): pop a session or a file preview out beside the dashboard from a native wrapper - #432
Conversation
…d from a native wrapper An Android WebView wrapper has no browser pop-ups, so a foldable could not show two sessions, or a session and a file, side by side. A wrapper that can open a window of its own now exposes window.CodemanHost.openWindow(url); detachSession, detachFilePreview and openWebviewExternal hand their URL to it, mobile.css keeps the pop-out icon under html.host-windows, and a solo window closes and raises itself through the host when it offers the calls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for this. The two-windows-on-a-foldable problem is real, and you found the honest reason it could not work: a WebView wrapper has no The code is careful. I read all three call sites and both CSS rules and it is genuinely inert without a host: One thing genuinely needs to exist before this lands, and then a handful of one-liners. 1. The seam is documented nowhere. CLAUDE.md says third-party integration surfaces live in
A short "Seam 5: native wrapper windows" section covering the three methods, the return contract and the BroadcastChannel requirement would do it, plus one line saying the contract is outside 2. 3. 4. No liveness at all when BroadcastChannel is missing ( 5. The host-aware default is duplicated ( Smaller:
One call that is mine rather than yours, and I want to be straight about it since you have done the work already: this adds a Send those up and I will merge. If you want this in the next release, items 2 through 5 are the ones I need; the docs section can follow if it would hold you up, though I would rather land them together. |
|
Hey, just checking in on this one. Are you still up for the changes from my review on the 15th? Items 2 to 5 are the ones I need before merging (the No pressure on timing. If you're busy and would rather I push those fixes onto your branch myself, just say so and I'll do it. |
Applies the review's landing list for the native-wrapper window bridge, with the verifier corrections. detachSession now refuses before asking the host when there is no window channel (no BroadcastChannel). Without the channel there is no roll-call liveness, so a hosted tab could never re-dock and would stay detached, and excluded from tiles and split, until the session ended. The guard sits before the host call so a channel-less host never gets a native window and a window.open as well. A single resolver, tabDetachButtonEnabled(), now lives in app.js next to hasHostWindows() and decides the host-aware pop-out default for the tab icon, App Settings and the tab action menu (which also serves the tile grid's menu). Before this the menu read the raw setting and hid "Open in a new window" under a host. The menu and both settings-ui.js sites call it optionally with a fallback, because test/session-sidebar-ux.browser.test.ts loads tab-rail-resize.js onto a bare CodemanApp without app.js, and a bare call would throw before the menu is appended. The "Close window" button on the solo session-gone overlay goes through _closeSoloWindow(), as the re-dock button already did, so it works in a host window. openWebviewExternal no longer falls through to window.open when the host refuses (in a WebView that can replace the dashboard page); it toasts instead, like the session and file-preview paths. The hasHostWindows and openInHostWindow JSDoc now say what the code does: anything but false counts as opened, and a saved web tab passes its own origin. docs/versioning-policy.md lists the window.CodemanHost bridge under experimental surfaces, so it does not read as a stable contract until the wrapper docs section lands. test/host-window-detach.test.ts gives the harness a live window channel (Object.create leaves it undefined, which the new guard would refuse) and pins the no-channel refusal. The per-PR changeset is removed; the release writes one consolidated changeset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thirteen contributor PRs, each re-checked against its GitHub head, merged with its own merge commit and landing fixes, reviewed, and gated together (524 test files, 10194 tests): #559, #552, #550, #556, #551, #546, #542, #540, #555, #543, #541, #502, #432. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged in 1.40.0, thanks @shenlvkang-collab! The bridge leaves browsers exactly as they were and only lights up under a host, which made it easy to land. Applied on the way in (f79f530): pop-out refuses before asking the host when there is no BroadcastChannel (without it the tab could never re-dock); one resolver, |
On a foldable, the natural way to watch two sessions, or a session and a file, is two windows side by side. A browser can pop a session out with
window.open, but an Android WebView wrapper has no pop-ups, andmobile.csshides the pop-out icon at those widths anyway. So a wrapper app had no way to reuse the solo window (/session/<id>) or the file viewer's detach route.This adds a small, opt-in host contract. An embedding app that can open a page in a window of its own exposes:
window.CodemanHost.openWindow(absoluteUrl)→ whether a window opened (required)window.CodemanHost.closeWindow()/focusWindow()(optional; script-initiatedwindow.close()/focus()do nothing in a window the page did notwindow.open)When
openWindowis present:detachSessionhands the solo URL to the host instead ofwindow.open, marks the tab detached, and announces it on the window channel. Since there is no WindowProxy to poll, liveness uses the same roll-call path a reloaded dashboard already uses. The solo window's announcements andpagehidekeep working unchanged. If the host refuses, the tab stays docked and a toast appears.detachFilePreviewandopenWebviewExternalalso go through the host.html.host-windowskeeps the tab pop-out icon at tablet widths (600–768px), andshowTabDetachButtondefaults on when unset. Phone tabs (≤599px) still hide it with!important, so the gear + close tap-zone arithmetic inmobile-tab-tap-zonesstays valid. At that width the host offers the pop-out from its own chrome viaapp.detachSession(id).close-requestgo throughcloseWindow()when the host offers it.Browsers define none of this, so desktop and mobile browser behaviour is unchanged.
openInHostWindowreturnsnullwhen there is no host, and every call site falls through to the existingwindow.opencode.Validation:
test/host-window-detach.test.ts(5 tests, vm-loadedapp.js): the host path, a refusing host, a throwing host, the no-host fallthrough, and solo close/focus via the host.test/file-preview-detach.test.tsgained 2 host cases.mobile-tab-tap-zonesandfoldable-layout.CodemanHost: at 700px the icon is visible and tapping it callsopenWindow('<origin>/session/<id>'); at 440px the icon is hidden andapp.detachSessiontakes the same path. Opening that URL in a second page gives solo mode, the dashboard tab staysdetachedacross a roll-call, the solo re-dock button callscloseWindow(), and after the page closes the dashboard redocks. Without a host at 700px and 1280px there is nohost-windowsclass and no icon (unchanged).FLAG_ACTIVITY_LAUNCH_ADJACENT) has not been done yet. I am not claiming a physical-foldable test.Includes a minor changeset. AI-assisted; implementation and validation reviewed locally.
🤖 Generated with Claude Code