mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-28 08:56:58 +00:00
Make the dashboard the default interface at root (#3345)
* feat: make the dashboard the default interface at root The dashboard now loads at / (with /dashboard kept as an alias), and the desktop moves to a new /desktop route. An "Open Desktop" button pinned at the bottom of the dashboard sidebar switches to the desktop. Root URLs that carry a desktop-only flow keep booting the desktop: auth popups (?embedded_in_popup=), app deep links (?app=), direct downloads (?download=), fullpage mode (?puter.fullpage=), and iframe embeds. Desktop-context redirects are retargeted so desktop users stay on the desktop: logout, the QR "continue on phone" link, the auth cover-page logo click, and window close/hide URL resets now use /desktop. OIDC login/signup gains a strictly whitelisted return_to param (/desktop or /dashboard) so social login returns to the interface it started from. Also: the dashboard strips ?ref= like the desktop does, ?auth_token= cleanup preserves the path and tab hash, the logged-out session list gets the wallpaper back, ?download= also works at /desktop, and the GUI dev server serves /desktop and /dashboard. * Add Open Desktop bento card and CSS Replace the previous desktop-switch UI with a full-width "Open Desktop" bento card that links to /desktop. Removes the pinned sidebar Open Desktop button and the old desktop-switch button/handler, and adds new CSS variables and styles for the desktop card (icon background, shadow, surface/header colors), hover shadow and arrow transition, and layout/padding adjustments. Changed files: TabHome.js (removed old switch, added anchor card), UIDashboard.js (removed sidebar button), dashboard.css (new desktop color vars and styles; removed legacy desktop-switch/button styles). * Remove box-shadow from myapps search input Delete the box-shadow declarations from input.myapps-search::placeholder in dashboard.css. This simplifies the placeholder styling (removes subtle and light shadow layers), likely to reduce visual noise or fix rendering inconsistency. * Show index_url hostname for opaque app IDs Only replace an app's title with the hostname from index_url when the app appears to use an opaque id. Add a check for names starting with 'app-' so the hostname is used only if title, name and uuid are identical and the name looks like an opaque app id. This avoids overwriting meaningful user-facing names that may coincidentally match the uuid and clarifies the intent in the comment. * Update dashboard.css
This commit is contained in:
@@ -173,12 +173,14 @@ describe('HomepageController shell routes', () => {
|
||||
expect(String(captured.body)).toMatch(/<!DOCTYPE html>/i);
|
||||
});
|
||||
|
||||
it('serves the shell on /settings, /dashboard, /action, /@:username', async () => {
|
||||
it('serves the shell on /settings, /dashboard, /desktop, /action, /@:username', async () => {
|
||||
for (const path of [
|
||||
'/settings',
|
||||
'/settings/*splat',
|
||||
'/dashboard',
|
||||
'/dashboard/',
|
||||
'/desktop',
|
||||
'/desktop/',
|
||||
'/action/*splat',
|
||||
'/@:username',
|
||||
]) {
|
||||
|
||||
@@ -82,6 +82,9 @@ export class HomepageController extends PuterController {
|
||||
router.get('/dashboard', {}, (req, res) => sendShell(req, res));
|
||||
router.get('/dashboard/', {}, (req, res) => sendShell(req, res));
|
||||
|
||||
router.get('/desktop', {}, (req, res) => sendShell(req, res));
|
||||
router.get('/desktop/', {}, (req, res) => sendShell(req, res));
|
||||
|
||||
router.get('/action/*splat', {}, (req, res) => sendShell(req, res));
|
||||
|
||||
router.get('/@:username', {}, (req, res) => sendShell(req, res));
|
||||
|
||||
@@ -139,6 +139,19 @@ export class OIDCController extends PuterController {
|
||||
|
||||
let appRedirectUri = flowRedirects[flow] ?? (origin || '/');
|
||||
|
||||
// Optional GUI return path so login started from /desktop or
|
||||
// /dashboard lands back there. Strict whitelist — never a
|
||||
// client-supplied URL (no open redirect).
|
||||
const rawReturnTo = Array.isArray(req.query.return_to)
|
||||
? req.query.return_to[0]
|
||||
: req.query.return_to;
|
||||
if (
|
||||
(flow === 'login' || flow === 'signup') &&
|
||||
(rawReturnTo === '/desktop' || rawReturnTo === '/dashboard')
|
||||
) {
|
||||
appRedirectUri = `${origin}${rawReturnTo}`;
|
||||
}
|
||||
|
||||
// Popup support
|
||||
const rawPopup = Array.isArray(req.query.embedded_in_popup)
|
||||
? req.query.embedded_in_popup[0]
|
||||
|
||||
@@ -52,7 +52,7 @@ startServer(1);
|
||||
// build the GUI
|
||||
build();
|
||||
|
||||
app.get(['/', '/app/*', '/action/*'], (req, res) => {
|
||||
app.get(['/', '/app/*', '/action/*', '/desktop', '/dashboard'], (req, res) => {
|
||||
res.send(generateDevHtml({
|
||||
env: env,
|
||||
api_origin: 'https://api.puter.com',
|
||||
|
||||
@@ -28,10 +28,12 @@ function buildRecentAppsHTML() {
|
||||
// Show up to 6 recent apps (2 columns x 3 rows)
|
||||
const recentApps = window.launch_apps.recent.slice(0, 6);
|
||||
for (const app_info of recentApps) {
|
||||
// if title, name and uuid are the same and index_url is set, then show the hostname of index_url
|
||||
// if title, name and uuid are the same and start with 'app-' and index_url
|
||||
// is set, then show the hostname of index_url instead of the opaque app id
|
||||
if (
|
||||
app_info.name === app_info.title &&
|
||||
app_info.name === app_info.uuid &&
|
||||
app_info.name?.startsWith('app-') &&
|
||||
app_info.index_url
|
||||
) {
|
||||
app_info.title = new URL(app_info.index_url).hostname;
|
||||
@@ -178,14 +180,6 @@ const TabHome = {
|
||||
h += '</div>';
|
||||
h += '</div>';
|
||||
|
||||
// Desktop switch card (spans full width)
|
||||
// h += '<div class="bento-card bento-desktop-switch">';
|
||||
// h += '<div class="bento-desktop-switch-inner">';
|
||||
// h += '<span class="bento-desktop-switch-text">Looking for Puter\'s desktop interface?</span>';
|
||||
// h += '<button class="bento-desktop-switch-btn">Switch to Desktop</button>';
|
||||
// h += '</div>';
|
||||
// h += '</div>';
|
||||
|
||||
// Usage card (spans full width on second row)
|
||||
h += '<div class="bento-card bento-usage">';
|
||||
h +=
|
||||
@@ -208,6 +202,23 @@ const TabHome = {
|
||||
h += '</div>';
|
||||
h += '</div>';
|
||||
|
||||
// Open Desktop card (spans full width, links to the desktop interface)
|
||||
h += '<a href="/desktop" target="_blank" rel="noopener" class="bento-card bento-desktop allow-native-ctxmenu">';
|
||||
h += '<div class="bento-card-fancy-icon bento-card-fancy-icon-desktop">';
|
||||
h +=
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>';
|
||||
h += '</div>';
|
||||
h += '<div class="bento-card-fancy-text">';
|
||||
h += `<h2>${i18n('open_desktop')}</h2>`;
|
||||
h += '<span class="bento-card-fancy-subtitle">';
|
||||
h +=
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>';
|
||||
h += 'Switch to the desktop interface';
|
||||
h += '</span>';
|
||||
h += '</div>';
|
||||
h += '<span class="bento-desktop-arrow">›</span>';
|
||||
h += '</a>';
|
||||
|
||||
h += '</div>';
|
||||
return h;
|
||||
},
|
||||
@@ -271,11 +282,6 @@ const TabHome = {
|
||||
},
|
||||
);
|
||||
|
||||
// Handle desktop switch button
|
||||
$el_window.on('click', '.bento-desktop-switch-btn', function () {
|
||||
window.location.href = '/';
|
||||
});
|
||||
|
||||
// Handle "Save Account" warning click
|
||||
$el_window.on('click', '.bento-save-account-warning', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -149,6 +149,11 @@ async function UIDashboard (options) {
|
||||
|
||||
const $el_window = $(el_window);
|
||||
|
||||
// Remove `?ref=...` from the URL, keeping the path and tab hash (mirrors UIDesktop)
|
||||
if ( window.url_query_params?.has('ref') ) {
|
||||
window.history.pushState(null, document.title, window.location.pathname + window.location.hash);
|
||||
}
|
||||
|
||||
// Restore sidebar collapsed state
|
||||
if ( localStorage.getItem('dashboard-sidebar-collapsed') === '1' ) {
|
||||
$el_window.find('.dashboard-sidebar').addClass('collapsed');
|
||||
@@ -285,8 +290,9 @@ async function UIDashboard (options) {
|
||||
window.addEventListener('hashchange', handleRouteChange);
|
||||
window.addEventListener('popstate', handleRouteChange);
|
||||
|
||||
// Sidebar item click handler
|
||||
$el_window.on('click', '.dashboard-sidebar-item', function (e) {
|
||||
// Sidebar item click handler — only tab items ([data-section]); plain links
|
||||
// like the "Open Desktop" button navigate natively
|
||||
$el_window.on('click', '.dashboard-sidebar-item[data-section]', function (e) {
|
||||
e.preventDefault();
|
||||
const $this = $(this);
|
||||
const section = $this.attr('data-section');
|
||||
|
||||
@@ -709,9 +709,9 @@ async function UIDesktop (options) {
|
||||
window.sidebar_items = val;
|
||||
});
|
||||
|
||||
// Remove `?ref=...` from navbar URL
|
||||
// Remove `?ref=...` from navbar URL, keeping the current path
|
||||
if ( window.url_query_params.has('ref') ) {
|
||||
window.history.pushState(null, document.title, '/');
|
||||
window.history.pushState(null, document.title, window.location.pathname);
|
||||
}
|
||||
|
||||
//show_hidden_files
|
||||
@@ -1732,7 +1732,7 @@ async function UIDesktop (options) {
|
||||
try {
|
||||
stat = await puter.fs.stat({ path: item_path, consistency: 'eventual' });
|
||||
} catch ( e ) {
|
||||
window.history.replaceState(null, document.title, '/');
|
||||
window.history.replaceState(null, document.title, '/desktop');
|
||||
UIAlert({
|
||||
message: i18n('error_user_or_path_not_found'),
|
||||
type: 'error',
|
||||
@@ -1816,9 +1816,11 @@ async function UIDesktop (options) {
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Direct download link
|
||||
// i.e. https://puter.com/?download=<file_url>
|
||||
// i.e. https://puter.com/?download=<file_url> or https://puter.com/desktop?download=<file_url>
|
||||
//--------------------------------------------------------------------------------------
|
||||
if ( window.url_paths.length === 0 && window.url_query_params.has('download') ) {
|
||||
const is_bare_desktop_path = window.url_paths.length === 0
|
||||
|| (window.url_paths.length === 1 && window.url_paths[0]?.toLocaleLowerCase() === 'desktop');
|
||||
if ( is_bare_desktop_path && window.url_query_params.has('download') ) {
|
||||
const url = window.url_query_params.get('download');
|
||||
let file_name = url.split('/').pop().split('?')[0];
|
||||
|
||||
@@ -2051,7 +2053,7 @@ $(document).on('contextmenu taphold', '.toolbar', function (event) {
|
||||
$(document).on('click', '.qr-btn', async function (e) {
|
||||
UIWindowQR({
|
||||
message_i18n_key: 'scan_qr_c2a',
|
||||
text: `${window.gui_origin }?auth_token=${ window.auth_token}`,
|
||||
text: `${window.gui_origin }/desktop?auth_token=${ window.auth_token}`,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3561,7 +3561,7 @@ $.fn.close = async function (options) {
|
||||
// only reset the URL if this window was the one that owns it (mirrors the open-side check in focusWindow)
|
||||
const update_window_url = $(this).attr('data-update_window_url');
|
||||
if ( ! window.is_dashboard_mode && (update_window_url === 'true' || update_window_url === null) ) {
|
||||
window.history.replaceState(null, document.title, '/');
|
||||
window.history.replaceState(null, document.title, '/desktop');
|
||||
}
|
||||
// bring focus to the last window in the window-stack (only if not minimized)
|
||||
let next_window_focused = false;
|
||||
@@ -4015,7 +4015,7 @@ $.fn.hideWindow = async function (options) {
|
||||
// update title and window URL — only if this window was the one that owns the URL
|
||||
const update_window_url = $(this).attr('data-update_window_url');
|
||||
if ( ! window.is_dashboard_mode && (update_window_url === 'true' || update_window_url === null) ) {
|
||||
window.history.replaceState(null, document.title, '/');
|
||||
window.history.replaceState(null, document.title, '/desktop');
|
||||
document.title = i18n('window_title_puter');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +381,8 @@ async function UIWindowLogin (options) {
|
||||
|
||||
if ( logo_clickable ) {
|
||||
$(el_window).find('.auth-logo').on('click', function () {
|
||||
window.location.href = '/';
|
||||
// keep desktop users on the desktop; everyone else goes to the root dashboard
|
||||
window.location.href = window.location.pathname === '/desktop' ? '/desktop' : '/';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -407,6 +408,12 @@ async function UIWindowLogin (options) {
|
||||
$(el_window).find(btnClass).on('click', function () {
|
||||
let url = `${window.gui_origin}/auth/oidc/${provider}/start?flow=login`;
|
||||
|
||||
// return to the interface the login started from (backend whitelists the path)
|
||||
const return_to = window.location.pathname;
|
||||
if ( return_to === '/desktop' || return_to === '/dashboard' ) {
|
||||
url += `&return_to=${encodeURIComponent(return_to)}`;
|
||||
}
|
||||
|
||||
const referrer = options.referrer ?? window.referrerStr ?? window.openerOrigin;
|
||||
if ( referrer ) {
|
||||
url += `&referrer=${encodeURIComponent(referrer)}`;
|
||||
|
||||
@@ -148,7 +148,8 @@ function UIWindowSignup (options) {
|
||||
}
|
||||
if ( logo_clickable ) {
|
||||
$(el_window).find('.auth-logo').on('click', function () {
|
||||
window.location.href = '/';
|
||||
// keep desktop users on the desktop; everyone else goes to the root dashboard
|
||||
window.location.href = window.location.pathname === '/desktop' ? '/desktop' : '/';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -191,6 +192,11 @@ function UIWindowSignup (options) {
|
||||
$(el_window).find(btnClass).css('display', 'flex');
|
||||
$(el_window).find(btnClass).on('click', function () {
|
||||
let url = `${window.gui_origin}/auth/oidc/${provider}/start?flow=signup`;
|
||||
// return to the interface the signup started from (backend whitelists the path)
|
||||
const return_to = window.location.pathname;
|
||||
if ( return_to === '/desktop' || return_to === '/dashboard' ) {
|
||||
url += `&return_to=${encodeURIComponent(return_to)}`;
|
||||
}
|
||||
const referrer = options.referrer ?? window.referrerStr;
|
||||
if ( referrer ) {
|
||||
url += `&referrer=${encodeURIComponent(referrer)}`;
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
--dashboard-bento-usage-surface: #eef2ff;
|
||||
--dashboard-bento-usage-header-bg: #e0e7ff;
|
||||
--dashboard-bento-usage-bar-fill: #ea580c;
|
||||
/* Emerald (matches the green icon accent used elsewhere) */
|
||||
--dashboard-bento-desktop-icon-bg: #059669;
|
||||
--dashboard-bento-desktop-icon-shadow: rgba(5, 150, 105, 0.22);
|
||||
--dashboard-bento-desktop-surface: #ecfdf5;
|
||||
--dashboard-bento-desktop-header-bg: #d1fae5;
|
||||
|
||||
--dashboard-shadow-subtle: rgba(0, 0, 0, 0.04);
|
||||
--dashboard-shadow-light: rgba(0, 0, 0, 0.06);
|
||||
@@ -166,6 +171,10 @@ body {
|
||||
--dashboard-bento-usage-surface: #1c1d2e;
|
||||
--dashboard-bento-usage-header-bg: #252a42;
|
||||
--dashboard-bento-usage-bar-fill: #fb923c;
|
||||
--dashboard-bento-desktop-icon-bg: #10b981;
|
||||
--dashboard-bento-desktop-icon-shadow: rgba(16, 185, 129, 0.32);
|
||||
--dashboard-bento-desktop-surface: #16241f;
|
||||
--dashboard-bento-desktop-header-bg: #172e26;
|
||||
|
||||
--dashboard-shadow-subtle: rgba(0, 0, 0, 0.2);
|
||||
--dashboard-shadow-light: rgba(0, 0, 0, 0.3);
|
||||
@@ -643,7 +652,7 @@ body {
|
||||
|
||||
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
|
||||
.myapps-search-wrap {
|
||||
background: rgba(255, 255, 255, 0.52);
|
||||
background: rgba(255, 255, 255, 0);
|
||||
backdrop-filter: blur(20px) saturate(1.5);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(1.5);
|
||||
}
|
||||
@@ -1074,9 +1083,6 @@ input.myapps-search::placeholder {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--dashboard-shadow-light);
|
||||
box-shadow:
|
||||
0 1px 2px var(--dashboard-shadow-subtle),
|
||||
0 4px 16px var(--dashboard-shadow-light);
|
||||
}
|
||||
|
||||
/* Welcome Card — uses same surface as .bento-card (no separate welcome tint) */
|
||||
@@ -1246,6 +1252,11 @@ input.myapps-search::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dashboard .bento-card-fancy-icon-desktop {
|
||||
background-color: var(--dashboard-bento-desktop-icon-bg);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dashboard .bento-card-fancy-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1386,46 +1397,39 @@ input.myapps-search::placeholder {
|
||||
color: var(--dashboard-text-tertiary);
|
||||
}
|
||||
|
||||
/* Desktop switch bento card */
|
||||
.dashboard .bento-desktop-switch {
|
||||
/* Open Desktop bento card (full width; links to the desktop interface) */
|
||||
.dashboard .bento-desktop {
|
||||
grid-column: 1 / -1;
|
||||
min-height: auto;
|
||||
background-color: var(--dashboard-bento-apps-surface);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop-switch-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop-switch-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--dashboard-text-secondary);
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop-switch-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--dashboard-background);
|
||||
background: var(--dashboard-icon-blue-start);
|
||||
border: 1px solid var(--dashboard-primary);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
gap: 14px;
|
||||
padding: 18px 24px;
|
||||
background-color: var(--dashboard-bento-desktop-header-bg);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
color: inherit;
|
||||
transition: box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.dashboard .bento-desktop-switch-btn:hover {
|
||||
background: var(--dashboard-icon-blue-end);
|
||||
color: white;
|
||||
.dashboard .bento-desktop:hover {
|
||||
box-shadow:
|
||||
0 2px 4px var(--dashboard-shadow-subtle),
|
||||
0 10px 28px var(--dashboard-bento-desktop-icon-shadow);
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop:hover .bento-desktop-arrow {
|
||||
transform: translateX(3px);
|
||||
color: var(--dashboard-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop-arrow {
|
||||
margin-left: auto;
|
||||
font-size: 22px;
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
color: var(--dashboard-text-hint);
|
||||
transition: transform 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
/* Usage bento card (cool indigo surfaces; orange bars stay as progress accent) */
|
||||
@@ -1701,6 +1705,11 @@ input.myapps-search::placeholder {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dashboard .bento-desktop {
|
||||
padding: 16px 20px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dashboard .bento-card-fancy-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
@@ -206,6 +206,7 @@ const en = {
|
||||
or: 'or',
|
||||
open: 'Open',
|
||||
new_window: 'New Window',
|
||||
open_desktop: 'Open Desktop',
|
||||
open_in_ai: 'Open in AI',
|
||||
open_in_new_tab: 'Open in New Tab',
|
||||
open_in_new_window: 'Open in New Window',
|
||||
|
||||
+36
-9
@@ -157,9 +157,29 @@ if ( jQuery ) {
|
||||
}
|
||||
|
||||
// are we in dashboard mode?
|
||||
if ( window.location.pathname === '/dashboard' || window.location.pathname === '/dashboard/' ) {
|
||||
window.is_dashboard_mode = true;
|
||||
window.dashboard_initial_route = parseDashboardRoute();
|
||||
// The dashboard is the default interface at the root path; `/dashboard` is kept as an
|
||||
// alias, and `/desktop` loads the desktop instead. Root URLs that carry a desktop-only
|
||||
// flow keep booting the desktop: auth popups (`?embedded_in_popup=`), app deep links
|
||||
// (`?app=`), direct downloads (`?download=`), fullpage mode (`?puter.fullpage=`), and
|
||||
// iframe embeds.
|
||||
{
|
||||
const pathname = window.location.pathname;
|
||||
const search_params = new URLSearchParams(window.location.search);
|
||||
if ( ['true', '1'].includes(search_params.get('embedded_in_popup')) ) {
|
||||
window.embedded_in_popup = true;
|
||||
}
|
||||
// note: iframe detection is inlined because globals.js (window.is_embedded) loads after this module
|
||||
const in_iframe = window.location !== window.parent.location;
|
||||
const needs_desktop_at_root = window.embedded_in_popup
|
||||
|| in_iframe
|
||||
|| search_params.has('puter.fullpage')
|
||||
|| search_params.has('app')
|
||||
|| search_params.has('download');
|
||||
const is_dashboard_alias = pathname === '/dashboard' || pathname === '/dashboard/';
|
||||
if ( is_dashboard_alias || (pathname === '/' && !needs_desktop_at_root) ) {
|
||||
window.is_dashboard_mode = true;
|
||||
window.dashboard_initial_route = parseDashboardRoute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,9 +451,9 @@ window.initgui = async function (options) {
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Is GUI embedded in a popup?
|
||||
// i.e. https://puter.com/?embedded_in_popup=true
|
||||
// (the flag itself is parsed once at module level, alongside the dashboard-mode check)
|
||||
//--------------------------------------------------------------------------------------
|
||||
if ( window.url_query_params.has('embedded_in_popup') && (window.url_query_params.get('embedded_in_popup') === 'true' || window.url_query_params.get('embedded_in_popup') === '1') ) {
|
||||
window.embedded_in_popup = true;
|
||||
if ( window.embedded_in_popup ) {
|
||||
$('body').addClass('embedded-in-popup');
|
||||
|
||||
// determine the origin of the opener (preserved across OIDC redirect via URL param, else referrer or messaging)
|
||||
@@ -720,8 +740,9 @@ window.initgui = async function (options) {
|
||||
// update auth data
|
||||
await window.update_auth_data(query_param_auth_token, whoami, api_origin);
|
||||
}
|
||||
// remove auth_token from URL
|
||||
window.history.pushState(null, document.title, '/');
|
||||
// remove auth_token from URL, keeping the current path (e.g. `/` or `/desktop`)
|
||||
// and hash (dashboard tab links like /#usage)
|
||||
window.history.pushState(null, document.title, window.location.pathname + window.location.hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,8 +866,9 @@ window.initgui = async function (options) {
|
||||
$('.taskbar').remove();
|
||||
// disable native browser exit confirmation
|
||||
window.onbeforeunload = null;
|
||||
// go to home page
|
||||
window.location.replace('/');
|
||||
// go back to the interface the user was in: dashboard users to the root
|
||||
// dashboard, desktop users to /desktop
|
||||
window.location.replace(window.is_dashboard_mode ? '/' : '/desktop');
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
@@ -1251,6 +1273,11 @@ window.initgui = async function (options) {
|
||||
const needs_action = action === 'authme' || action === 'copyauth';
|
||||
const reload_on_success = needs_action;
|
||||
if ( window.logged_in_users.length > 0 ) {
|
||||
// dashboard mode skips the wallpaper (fullpage), but the session list
|
||||
// has no cover page — restore the wallpaper so it isn't on a blank page
|
||||
if ( window.is_dashboard_mode ) {
|
||||
window.refresh_desktop_background();
|
||||
}
|
||||
await UIWindowSessionList({
|
||||
redirect_url: needs_action ? window.location.href : undefined,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user