feat(mobile): pop a session or a file preview out beside the dashboard 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>
This commit is contained in:
shenlvkang-collab
2026-09-15 20:19:47 +08:00
co-authored by Claude Opus 5
parent 88e3faa456
commit 0720526b64
9 changed files with 253 additions and 8 deletions
+24
View File
@@ -128,6 +128,30 @@ describe('file viewer detach button', () => {
expect(app.showToast).toHaveBeenCalledWith(expect.stringContaining('Pop-up blocked'), 'error');
});
it('hands the URL to a host window opener instead of window.open', () => {
const { app, overlay, windowStub } = loadApp();
app.openInHostWindow = vi.fn().mockReturnValue(true);
app.filePreviewDetachUrl = '/api/sessions/s1/file-raw?path=doc.pdf';
app.detachFilePreview();
expect(app.openInHostWindow).toHaveBeenCalledWith('/api/sessions/s1/file-raw?path=doc.pdf');
expect(windowStub.open).not.toHaveBeenCalled();
expect(overlay.classList.contains('visible')).toBe(false);
});
it('keeps the overlay and toasts when the host could not open a window', () => {
const { app, overlay, windowStub } = loadApp();
app.openInHostWindow = vi.fn().mockReturnValue(false);
app.filePreviewDetachUrl = '/api/sessions/s1/file-raw?path=doc.pdf';
app.detachFilePreview();
expect(windowStub.open).not.toHaveBeenCalled();
expect(overlay.classList.contains('visible')).toBe(true);
expect(app.showToast).toHaveBeenCalledWith(expect.stringContaining('Could not open'), 'error');
});
it('does nothing when no preview is armed', () => {
const { app, windowStub } = loadApp();
app.filePreviewDetachUrl = '';