mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 09:19:42 +02:00
fix(mobile): #432 landing fixes
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>
This commit is contained in:
+21
-6
@@ -1673,8 +1673,11 @@ class CodemanApp {
|
||||
// open the solo URL in a window of its own, beside this one on a foldable or
|
||||
// a split screen. There is no WindowProxy to poll, so the tab is tracked the
|
||||
// way a dashboard reload tracks it: the solo window's channel announcements
|
||||
// plus the roll-call liveness check.
|
||||
const hosted = this.openInHostWindow(CodemanBase.url('/session/' + encodeURIComponent(id)));
|
||||
// plus the roll-call liveness check. Without a channel there is no roll-call
|
||||
// either, so a hosted tab could never re-dock: refuse before asking the host.
|
||||
const hosted = this.hasHostWindows() && !this.windowChannel
|
||||
? false
|
||||
: this.openInHostWindow(CodemanBase.url('/session/' + encodeURIComponent(id)));
|
||||
if (hosted !== null) {
|
||||
if (!hosted) {
|
||||
this.showToast?.('Could not open a new window for this session', 'error');
|
||||
@@ -1700,8 +1703,8 @@ class CodemanApp {
|
||||
|
||||
/**
|
||||
* The embedding app's window opener, when there is one. A native wrapper
|
||||
* exposes `window.CodemanHost.openWindow(absoluteUrl)` (returning whether a
|
||||
* window opened) to say it can put a page in a window of its own; browsers
|
||||
* exposes `window.CodemanHost.openWindow(absoluteUrl)` (anything but false
|
||||
* counts as opened) to say it can put a page in a window of its own; browsers
|
||||
* never define it.
|
||||
* @returns {boolean} whether a host window opener is present
|
||||
*/
|
||||
@@ -1712,7 +1715,19 @@ class CodemanApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a same-origin page in a host window.
|
||||
* The tab pop-out setting, defaulting ON under a host that opens windows.
|
||||
* The one resolver for the tab icon, App Settings and the tab action menu.
|
||||
* @param {object} settings stored per-device App Settings
|
||||
* @param {object} [defaults] the device's default settings
|
||||
* @returns {boolean} whether the pop-out control shows
|
||||
*/
|
||||
tabDetachButtonEnabled(settings, defaults = {}) {
|
||||
return settings?.showTabDetachButton ?? (this.hasHostWindows() || (defaults?.showTabDetachButton ?? false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an http(s) URL in a host window, usually Codeman's own origin (a saved
|
||||
* web tab passes its own).
|
||||
* @param {string} url absolute or base-relative URL
|
||||
* @returns {boolean|null} null when there is no host (use window.open),
|
||||
* otherwise whether the host opened a window
|
||||
@@ -1949,7 +1964,7 @@ class CodemanApp {
|
||||
el.className = 'solo-gone-overlay';
|
||||
el.innerHTML = '<h2>Session unavailable</h2>'
|
||||
+ '<p>This session has ended or is no longer available.</p>'
|
||||
+ '<button class="btn-primary" onclick="window.close()">Close window</button>';
|
||||
+ '<button class="btn-primary" onclick="app._closeSoloWindow()">Close window</button>';
|
||||
document.body.appendChild(el);
|
||||
document.title = (window.codemanT?.('Session ended') || 'Session ended')
|
||||
+ ' — ' + (window.CodemanI18n?.displayName || 'Codeman');
|
||||
|
||||
Reference in New Issue
Block a user