diff --git a/packages/backend/src/om/mappings/app.js b/packages/backend/src/om/mappings/app.js index da00445a..4b50e43b 100644 --- a/packages/backend/src/om/mappings/app.js +++ b/packages/backend/src/om/mappings/app.js @@ -51,6 +51,7 @@ module.exports = { maxlen: 7000, }, maximize_on_start: 'flag', + background: 'flag', subdomain: { type: 'string', transient: true, diff --git a/packages/backend/src/routers/apps.js b/packages/backend/src/routers/apps.js index dbc470ec..9212cad4 100644 --- a/packages/backend/src/routers/apps.js +++ b/packages/backend/src/routers/apps.js @@ -72,6 +72,7 @@ router.get('/apps', auth, express.json({limit: '50mb'}), async (req, res, next)= icon: apps_res[i].icon, index_url: apps_res[i].index_url, godmode: apps_res[i].godmode, + background: apps_res[i].background, maximize_on_start: apps_res[i].maximize_on_start, filetype_associations: filetype_associations, ...stats, @@ -111,6 +112,7 @@ router.get('/apps/:name', auth, express.json({limit: '50mb'}), async (req, res, title: app.title, icon: app.icon, godmode: app.godmode, + background: app.background, maximize_on_start: app.maximize_on_start, index_url: app.index_url, }; diff --git a/packages/backend/src/services/database/SqliteDatabaseAccessService.js b/packages/backend/src/services/database/SqliteDatabaseAccessService.js index c54d29ef..408376db 100644 --- a/packages/backend/src/services/database/SqliteDatabaseAccessService.js +++ b/packages/backend/src/services/database/SqliteDatabaseAccessService.js @@ -42,7 +42,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { this.db = new Database(this.config.path); // Database upgrade logic - const TARGET_VERSION = 2; + const TARGET_VERSION = 3; if ( do_setup ) { this.log.noticeme(`SETUP: creating database at ${this.config.path}`); @@ -51,6 +51,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { '0002_add-default-apps.sql', '0003_user-permissions.sql', '0004_sessions.sql', + '0005_background-apps.sql', ].map(p => path_.join(__dirname, 'sqlite_setup', p)); const fs = require('fs'); for ( const filename of sql_files ) { @@ -75,6 +76,10 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { upgrade_files.push('0004_sessions.sql'); } + if ( user_version <= 2 ) { + upgrade_files.push('0005_background-apps.sql'); + } + if ( upgrade_files.length > 0 ) { this.log.noticeme(`Database out of date: ${this.config.path}`); this.log.noticeme(`UPGRADING DATABASE: ${user_version} -> ${TARGET_VERSION}`); diff --git a/packages/backend/src/services/database/sqlite_setup/0005_background-apps.sql b/packages/backend/src/services/database/sqlite_setup/0005_background-apps.sql new file mode 100644 index 00000000..cc3c2385 --- /dev/null +++ b/packages/backend/src/services/database/sqlite_setup/0005_background-apps.sql @@ -0,0 +1 @@ +ALTER TABLE apps ADD COLUMN "background" BOOLEAN DEFAULT 0; diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js index 3134c23e..48d4a68c 100644 --- a/src/UI/UIWindow.js +++ b/src/UI/UIWindow.js @@ -110,6 +110,8 @@ async function UIWindow(options) { options.window_css = options.window_css ?? {}; options.window_class = (options.window_class !== undefined ? ' ' + options.window_class : ''); + options.is_visible = options.is_visible ?? true; + // if only one instance is allowed, bring focus to the window that is already open if(options.single_instance && options.app !== ''){ let $already_open_window = $(`.window[data-app="${html_encode(options.app)}"]`); @@ -489,7 +491,9 @@ async function UIWindow(options) { $(el_window_head_icon).attr('src', window.icons['shared.svg']); } // focus on this window and deactivate other windows - $(el_window).focusWindow(); + if ( options.is_visible ) { + $(el_window).focusWindow(); + } if (animate_window_opening) { // animate window opening @@ -517,25 +521,28 @@ async function UIWindow(options) { // onAppend() - using show() is a hack to make sure window is visible AND onAppend is called when // window is actually appended and usable. - $(el_window).show(0, function(e){ - // if SaveFileDialog, bring focus to the el_savefiledialog_filename and select all - if(options.is_saveFileDialog){ - let item_name = el_savefiledialog_filename.value; - const extname = path.extname('/' + item_name); - if(extname !== '') - el_savefiledialog_filename.setSelectionRange(0, item_name.length - extname.length) - else - $(el_savefiledialog_filename).select(); - - $(el_savefiledialog_filename).get(0).focus({preventScroll:true}); - } - //set custom window css - $(el_window).css(options.window_css); - // onAppend() - if(options.onAppend && typeof options.onAppend === 'function'){ - options.onAppend(el_window); - } - }) + // NOTE: there is another is_visible condition below + if ( options.is_visible ) { + $(el_window).show(0, function(e){ + // if SaveFileDialog, bring focus to the el_savefiledialog_filename and select all + if(options.is_saveFileDialog){ + let item_name = el_savefiledialog_filename.value; + const extname = path.extname('/' + item_name); + if(extname !== '') + el_savefiledialog_filename.setSelectionRange(0, item_name.length - extname.length) + else + $(el_savefiledialog_filename).select(); + + $(el_savefiledialog_filename).get(0).focus({preventScroll:true}); + } + //set custom window css + $(el_window).css(options.window_css); + // onAppend() + if(options.onAppend && typeof options.onAppend === 'function'){ + options.onAppend(el_window); + } + }); + } if(options.is_saveFileDialog){ //------------------------------------------------ @@ -960,7 +967,9 @@ async function UIWindow(options) { $(el_window).css('top', options.top) $(el_window).css('left', options.left) } - $(el_window).css('display', 'block'); + if ( options.is_visible ) { + $(el_window).css('display', 'block'); + } // mousedown on the window body will unselect selected items if neither ctrl nor command are pressed $(el_window_body).on('mousedown', function(e){ diff --git a/src/helpers.js b/src/helpers.js index f3a88a9b..ce292aee 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1944,9 +1944,16 @@ window.launch_app = async (options)=>{ // iframe_url //----------------------------------- let iframe_url; + + // This can be any trusted URL that won't be used for other apps + const BUILTIN_PREFIX = 'https://builtins.namespaces.puter.com/'; + if(!app_info.index_url){ iframe_url = new URL('https://'+options.name+'.' + window.app_domain + `/index.html`); - }else{ + } else if ( app_info.index_url.startsWith(BUILTIN_PREFIX) ) { + const name = app_info.index_url.slice(BUILTIN_PREFIX.length); + iframe_url = new URL(`${gui_origin}/builtin/${name}`); + } else { iframe_url = new URL(app_info.index_url); } @@ -2026,7 +2033,9 @@ window.launch_app = async (options)=>{ // ...and finally append urm_source=puter.com to the URL iframe_url.searchParams.append('urm_source', 'puter.com'); - UIWindow({ + console.log('backgrounded??', app_info.background); + + const el_win = UIWindow({ element_uuid: uuid, title: title, iframe_url: iframe_url.href, @@ -2039,11 +2048,16 @@ window.launch_app = async (options)=>{ height: options.maximized ? `calc(100% - ${window.taskbar_height + window.toolbar_height + 1}px)` : undefined, width: options.maximized ? `100%` : undefined, app: options.name, + is_visible: ! app_info.background, is_maximized: options.maximized, is_fullpage: options.is_fullpage, ...window_options, }); + if ( ! app_info.background ) { + $(el_win).show(); + } + // send post request to /rao to record app open if(options.name !== 'explorer'){ // add the app to the beginning of the array