Dashboard: an app launched by another app takes over the tab

In dashboard mode apps are full-tab experiences, but an app launched by
another app (puter.ui.launchApp) opened as a floating titlebar window
over its full-tab parent: the parent stayed on screen around its edges,
couldn't be raised (focusWindow never raises stay_on_top windows), and a
child with no Apps-tab tile had no switcher to come back to once
minimized.

Give child launches the same treatment as tile launches — maximized, so
they get the headless chrome and control drawer — and minimize the
parent behind them, iOS-style. State only: the parent's /app/<name>
history entry already sits beneath the child's, so Back from the child
(and the child's close, which consumes its own entry) lands on the
parent's entry and the existing popstate handler restores it. The parent
keeps running while hidden, so parent/child IPC is unaffected.

Explorer keeps its windowed form, background apps don't minimize their
parent, and an explicit `maximized` option still wins. Desktop mode is
untouched: both changes are gated on is_dashboard_mode, so an app
launching an app there still gets a floating child over a visible
parent.
This commit is contained in:
jelveh
2026-07-29 21:36:47 -07:00
parent dc371fc4eb
commit fbf1bd18b1
+25
View File
@@ -271,6 +271,16 @@ const launch_app = async (options) => {
if ( app_info.maximize_on_start ) {
options.maximized = 1;
}
// Dashboard mode: apps are full-tab experiences (headless chrome,
// control drawer, tile/Back switching), so every app launch defaults
// to maximized — matching tile launches. Without this, an app launched
// by another app (puter.ui.launchApp) opened as a floating titlebar
// window over the full-tab parent, with no tile to switch back to.
// Explorer keeps its windowed form; an explicit option still wins.
if ( window.is_dashboard_mode && options.maximized === undefined
&& options.name !== 'explorer' && options.name !== 'trash' ) {
options.maximized = 1;
}
//-----------------------------------
// if opened a file, sign it
//-----------------------------------
@@ -701,6 +711,21 @@ const launch_app = async (options) => {
const el = await el_win;
process.references.el_win = el;
// Dashboard mode: an app launched from another app takes over the tab,
// iOS-style — the parent app minimizes behind it. State only: the
// parent's /app/<name> history entry already sits beneath the child's,
// so Back from the child (and the child's close, which consumes its
// entry) lands on the parent's entry and the popstate handler restores
// it. The parent keeps running while hidden — parent/child IPC works.
if ( window.is_dashboard_mode && options.parent_instance_id
&& options.maximized && !app_info.background && el ) {
const el_parent_win = window.window_for_app_instance(options.parent_instance_id);
const parent_minimized = $(el_parent_win).attr('data-is_minimized');
if ( el_parent_win && parent_minimized !== '1' && parent_minimized !== 'true' ) {
$(el_parent_win).hideWindow();
}
}
if ( ! options.launched_by_exec_service ) {
process.onchange('ipc_status', value => {
if ( value !== PROCESS_IPC_ATTACHED ) return;