mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-24 23:17:23 +00:00
Review round: pill/dashboard edge-case fixes
- Latch pop_dashboard_app_url while its history.back() is in flight: a double-click on minimize (or a close racing a minimize) issued two back()s, and the second popped the dashboard's own entry — navigating clean out of the page. Any popstate or push settles the latch. - Close a running app's windows when it is uninstalled: the tile is a headless app's only switcher, so a minimized instance would otherwise be stranded — impossible to restore or quit. - puter.ui.setWindowTitle in dashboard mode now also updates the browser-tab title (while that app owns the URL) and the window's data-name, so a later restore re-applies the app's current title instead of its launch-time one. Desktop behavior unchanged. - Nudge collapsed-pill contrast (opacity 0.55 → 0.65, border alpha 0.16 → 0.22) so the capsule stays findable over dark app content.
This commit is contained in:
@@ -618,6 +618,17 @@ const ipc_listener = async (event, handled) => {
|
||||
// floating control pill instead of a head)
|
||||
$(el_window).find('.window-head-title').html(html_encode(event.data.new_title));
|
||||
$(el_window).find('.dashboard-app-pill-title').text(event.data.new_title);
|
||||
// In dashboard mode the app's title is also the browser-tab title,
|
||||
// and data-name is what a restore re-applies (see showWindow's
|
||||
// push) — keep both fresh. The tab title only changes while this
|
||||
// window's app actually owns the URL.
|
||||
if ( window.is_dashboard_mode ) {
|
||||
$(el_window).attr('data-name', event.data.new_title);
|
||||
const app = $(el_window).attr('data-app');
|
||||
if ( app && window.location.pathname === `/app/${encodeURIComponent(app)}` ) {
|
||||
document.title = event.data.new_title;
|
||||
}
|
||||
}
|
||||
// send confirmation to requester window
|
||||
target_iframe.contentWindow.postMessage({
|
||||
original_msg_id: msg_id,
|
||||
|
||||
@@ -182,6 +182,11 @@ function showUninstallModal ({ appName, appTitle, appUid, self, $el_window }) {
|
||||
// restores its position if it comes back.
|
||||
const removedIndex = self._apps.findIndex(a => a.name === appName);
|
||||
const removedApp = removedIndex === -1 ? null : self._apps[removedIndex];
|
||||
// A running instance would be stranded: the tile is a headless
|
||||
// app's only switcher, so once it's gone a minimized window could
|
||||
// never be restored OR quit. Close the app's windows first (close
|
||||
// also consumes the app's URL entry if it owns one).
|
||||
$(`.window[data-app="${html_encode(appName)}"]`).close();
|
||||
self._invalidateInFlightLoads();
|
||||
close();
|
||||
|
||||
|
||||
@@ -4159,6 +4159,7 @@ function push_dashboard_app_url (app_name, title) {
|
||||
}
|
||||
window.history.pushState({ dashboard_app: app_name }, '', `/app/${encodeURIComponent(app_name)}`);
|
||||
dashboard_url_app = app_name;
|
||||
dashboard_url_pop_pending = false;
|
||||
if ( title ) document.title = title;
|
||||
}
|
||||
|
||||
@@ -4171,15 +4172,28 @@ function push_dashboard_app_url (app_name, title) {
|
||||
* when this app doesn't own the URL (caller falls back to hiding
|
||||
* directly, e.g. an app stacked under another app's entry).
|
||||
*/
|
||||
// True while a pop's history.back() is in flight (issued but its
|
||||
// popstate not yet processed). The URL doesn't change until the popstate
|
||||
// lands, so without this latch a double-click on minimize — or a close
|
||||
// racing a minimize — would issue TWO back()s, and the second would pop
|
||||
// the dashboard's own entry and navigate clean out of the page.
|
||||
let dashboard_url_pop_pending = false;
|
||||
|
||||
function pop_dashboard_app_url (app_name) {
|
||||
if ( ! window.is_dashboard_mode || ! app_name ) return false;
|
||||
if ( dashboard_app_url_current() !== app_name ) return false;
|
||||
// Duplicate request for an entry already being popped: report it
|
||||
// handled so the caller doesn't ALSO hide the window.
|
||||
if ( dashboard_url_pop_pending ) return true;
|
||||
dashboard_url_pop_pending = true;
|
||||
window.history.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
if ( ! window.is_dashboard_mode ) return;
|
||||
// Any traversal settles a pending pop (see pop_dashboard_app_url).
|
||||
dashboard_url_pop_pending = false;
|
||||
const new_app = dashboard_app_url_current();
|
||||
const prev_app = dashboard_url_app;
|
||||
// Same app on both sides means the traversal wasn't ours (e.g. an app
|
||||
|
||||
@@ -4242,7 +4242,7 @@ body.dashboard-mode .window-dashboard-headless .window-body-app {
|
||||
background: rgba(24, 24, 28, 0.62);
|
||||
-webkit-backdrop-filter: blur(14px) saturate(140%);
|
||||
backdrop-filter: blur(14px) saturate(140%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28), 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
color: #fff;
|
||||
-webkit-user-select: none;
|
||||
@@ -4276,7 +4276,9 @@ body.dashboard-mode .window-dashboard-headless .window-body-app {
|
||||
}
|
||||
|
||||
.dashboard-app-pill.collapsed {
|
||||
opacity: 0.55;
|
||||
/* Subtle but findable over both light and dark app content — the
|
||||
white hairline border carries the dark-on-dark case. */
|
||||
opacity: 0.65;
|
||||
padding: 3px 12px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user