diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
new file mode 100644
index 000000000..ba1aa4360
--- /dev/null
+++ b/.github/workflows/docker-image.yaml
@@ -0,0 +1,79 @@
+#
+name: Docker Image CI
+
+# Configures this workflow to run every time a change is pushed to the
+# branch called `main`.
+on:
+ push:
+ branches: ['main']
+
+
+# Defines two custom environment variables for the workflow. These are used
+# for the Container registry domain, and a name for the Docker image that
+# this workflow builds.
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
+
+# There is a single job in this workflow. It's configured to run on the
+# latest available version of Ubuntu.
+jobs:
+ build-and-push-image:
+ runs-on: ubuntu-latest
+
+ # Sets the permissions granted to the `GITHUB_TOKEN` for the actions
+ # in this job.
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Uses the `docker/login-action` action to log in to the Container
+ # registry using the account and password that will publish the packages.
+ # Once published, the packages are scoped to the account defined here.
+ - name: Log in to GitHub Package Container registry
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about)
+ # to extract tags and labels that will be applied to the specified image.
+ # The `id` "meta" allows the output of this step to be referenced in
+ # a subsequent step. The `images` value provides the base name for the
+ # tags and labels.
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=schedule
+ type=ref,event=branch
+ type=ref,event=pr
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}.{{minor}}
+ type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
+ type=sha
+ type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
+
+ # This step uses the `docker/build-push-action` action to build the
+ # image, based on your repository's `Dockerfile`. If the build succeeds,
+ # it pushes the image to GitHub Packages.
+ # It uses the `context` parameter to define the build's context as the
+ # set of files located in the specified path. For more information, see
+ # "[Usage](https://github.com/docker/build-push-action#usage)" in the
+ # README of the `docker/build-push-action` repository.
+ # It uses the `tags` and `labels` parameters to tag and label the image
+ # with the output from the "meta" step.
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js
index 2926f433d..c25b69fd2 100644
--- a/src/UI/UIDesktop.js
+++ b/src/UI/UIDesktop.js
@@ -33,6 +33,7 @@ import UIWindowLogin from "./UIWindowLogin.js"
import UIWindowQR from "./UIWindowQR.js"
import UIWindowRefer from "./UIWindowRefer.js"
import UITaskbar from "./UITaskbar.js"
+import new_context_menu_item from "../helpers/new_context_menu_item.js"
async function UIDesktop(options){
let h = '';
@@ -707,7 +708,7 @@ async function UIDesktop(options){
// -------------------------------------------
// New File
// -------------------------------------------
- window.new_context_menu_item(desktop_path, el_desktop),
+ new_context_menu_item(desktop_path, el_desktop),
// -------------------------------------------
// -
// -------------------------------------------
@@ -930,6 +931,7 @@ async function UIDesktop(options){
name: app_launched_from_url,
readURL: qparams.get('readURL'),
maximized: qparams.get('maximized'),
+ params: app_query_params ?? [],
is_fullpage: window.is_fullpage_mode,
window_options: {
stay_on_top: false,
diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js
index 3a90926fe..6e5340b7a 100644
--- a/src/UI/UIWindow.js
+++ b/src/UI/UIWindow.js
@@ -24,6 +24,7 @@ import UITaskbarItem from './UITaskbarItem.js';
import UIWindowLogin from './UIWindowLogin.js';
import UIWindowPublishWebsite from './UIWindowPublishWebsite.js';
import UIWindowItemProperties from './UIWindowItemProperties.js';
+import new_context_menu_item from '../helpers/new_context_menu_item.js';
const el_body = document.getElementsByTagName('body')[0];
@@ -1898,7 +1899,7 @@ async function UIWindow(options) {
// -------------------------------------------
// New
// -------------------------------------------
- window.new_context_menu_item($(el_window).attr('data-path'), el_window_body),
+ new_context_menu_item($(el_window).attr('data-path'), el_window_body),
// -------------------------------------------
// -
// -------------------------------------------
diff --git a/src/globals.js b/src/globals.js
index fa5e966f9..264adb1b1 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -141,8 +141,6 @@ if (window.location !== window.parent.location) {
window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
window.desktop_width = window.innerWidth;
-let isResize = false;
-
// recalculate desktop height and width on window resize
$( window ).on( "resize", function() {
const new_desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
diff --git a/src/helpers.js b/src/helpers.js
index a12fb405a..7b0b795ed 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -1904,6 +1904,7 @@ window.launch_app = async (options)=>{
// add app_instance_id to URL
iframe_url.searchParams.append('puter.app_instance_id', uuid);
+
// add app_id to URL
iframe_url.searchParams.append('puter.app.id', app_info.uuid);
@@ -1939,6 +1940,7 @@ window.launch_app = async (options)=>{
else if(options.token){
iframe_url.searchParams.append('puter.auth.token', options.token);
}
+
// Try to acquire app token from the server
else{
let response = await fetch(window.api_origin + "/auth/get-user-app-token", {
@@ -1977,13 +1979,6 @@ window.launch_app = async (options)=>{
window_class: 'window-app',
update_window_url: true,
app_uuid: app_info.uuid ?? app_info.uid,
- // has_head: options.has_head ?? true,
- // top: options.top ?? undefined,
- // left: options.left ?? undefined,
- // width: options.width ?? undefined,
- // height: options.height ?? undefined,
- // is_resizable: options.is_resizable ?? undefined,
- // window_css: options.window_css ?? undefined,
top: options.maximized ? 0 : undefined,
left: options.maximized ? 0 : undefined,
height: options.maximized ? `calc(100% - ${window.taskbar_height + window.toolbar_height + 1}px)` : undefined,
@@ -2241,63 +2236,6 @@ window.open_item = async function(options){
}
}
-/**
- * Returns a context menu item to create a new file/folder.
- *
- * @param {string} dirname - The directory path to create the item in
- * @param {HTMLElement} append_to_element - Element to append the new item to
- * @returns {Object} The context menu item object
- */
-
-window.new_context_menu_item = function(dirname, append_to_element){
- return {
- html: "New",
- items: [
- // New Folder
- {
- html: "New Folder",
- icon: `
`,
- onClick: function(){
- create_folder(dirname, append_to_element);
- }
- },
- // divider
- '-',
- // Text Document
- {
- html: `Text Document`,
- icon: `
`,
- onClick: async function(){
- create_file({dirname: dirname, append_to_element: append_to_element, name: 'New File.txt'});
- }
- },
- // HTML Document
- {
- html: `HTML Document`,
- icon: `
`,
- onClick: async function(){
- create_file({dirname: dirname, append_to_element: append_to_element, name: 'New File.html'});
- }
- },
- // JPG Image
- {
- html: `JPG Image`,
- icon: `
`,
- onClick: async function(){
- var canvas = document.createElement("canvas");
-
- canvas.width = 800;
- canvas.height = 600;
-
- canvas.toBlob((blob) =>{
- create_file({dirname: dirname, append_to_element: append_to_element, name: 'New Image.jpg', content: blob});
- });
- }
- },
- ]
- }
-}
-
/**
* Moves the given items to the destination path.
*
@@ -3125,29 +3063,6 @@ window.getUsage = () => {
}
-window.determine_active_container_parent = function(){
- // the container is either an ancestor of active element...
- let parent_container = $(active_element).closest('.item-container');
- // ... or a descendant of it...
- if(parent_container.length === 0){
- parent_container = $(active_element).find('.item-container');
- }
- // ... or siblings or cousins
- if(parent_container.length === 0){
- parent_container = $(active_element).closest('.window').find('.item-container');
- }
- // ... or the active element itself (if it's a container)
- if(parent_container.length === 0 && active_element && $(active_element).hasClass('item-container')){
- parent_container = $(active_element);
- }
- // ... or if there is no active element, the selected item that is not blurred
- if(parent_container.length === 0 && active_item_container){
- parent_container = active_item_container;
- }
-
- return parent_container;
-}
-
window.getAppUIDFromOrigin = async function(origin) {
try {
const response = await fetch(window.api_origin + "/auth/app-uid-from-origin", {
diff --git a/src/helpers/determine_active_container_parent.js b/src/helpers/determine_active_container_parent.js
new file mode 100644
index 000000000..32a8388d6
--- /dev/null
+++ b/src/helpers/determine_active_container_parent.js
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+const determine_active_container_parent = function(){
+ // the container is either an ancestor of active element...
+ let parent_container = $(active_element).closest('.item-container');
+ // ... or a descendant of it...
+ if(parent_container.length === 0){
+ parent_container = $(active_element).find('.item-container');
+ }
+ // ... or siblings or cousins
+ if(parent_container.length === 0){
+ parent_container = $(active_element).closest('.window').find('.item-container');
+ }
+ // ... or the active element itself (if it's a container)
+ if(parent_container.length === 0 && active_element && $(active_element).hasClass('item-container')){
+ parent_container = $(active_element);
+ }
+ // ... or if there is no active element, the selected item that is not blurred
+ if(parent_container.length === 0 && active_item_container){
+ parent_container = active_item_container;
+ }
+
+ return parent_container;
+}
+
+export default determine_active_container_parent;
\ No newline at end of file
diff --git a/src/helpers/new_context_menu_item.js b/src/helpers/new_context_menu_item.js
new file mode 100644
index 000000000..28f22298c
--- /dev/null
+++ b/src/helpers/new_context_menu_item.js
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+/**
+ * Returns a context menu item to create a new folder and a variety of file types.
+ *
+ * @param {string} dirname - The directory path to create the item in
+ * @param {HTMLElement} append_to_element - Element to append the new item to
+ * @returns {Object} The context menu item object
+ */
+
+const new_context_menu_item = function(dirname, append_to_element){
+ return {
+ html: "New",
+ items: [
+ // New Folder
+ {
+ html: "New Folder",
+ icon: `
`,
+ onClick: function(){
+ create_folder(dirname, append_to_element);
+ }
+ },
+ // divider
+ '-',
+ // Text Document
+ {
+ html: `Text Document`,
+ icon: `
`,
+ onClick: async function(){
+ create_file({dirname: dirname, append_to_element: append_to_element, name: 'New File.txt'});
+ }
+ },
+ // HTML Document
+ {
+ html: `HTML Document`,
+ icon: `
`,
+ onClick: async function(){
+ create_file({dirname: dirname, append_to_element: append_to_element, name: 'New File.html'});
+ }
+ },
+ // JPG Image
+ {
+ html: `JPG Image`,
+ icon: `
`,
+ onClick: async function(){
+ var canvas = document.createElement("canvas");
+
+ canvas.width = 800;
+ canvas.height = 600;
+
+ canvas.toBlob((blob) =>{
+ create_file({dirname: dirname, append_to_element: append_to_element, name: 'New Image.jpg', content: blob});
+ });
+ }
+ },
+ ]
+ }
+}
+
+export default new_context_menu_item;
\ No newline at end of file
diff --git a/src/initgui.js b/src/initgui.js
index 097dd0374..eada45ffc 100644
--- a/src/initgui.js
+++ b/src/initgui.js
@@ -33,6 +33,7 @@ import UIWindowChangeUsername from './UI/UIWindowChangeUsername.js';
import update_last_touch_coordinates from './helpers/update_last_touch_coordinates.js';
import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js';
import PuterDialog from './UI/PuterDialog.js';
+import determine_active_container_parent from './helpers/determine_active_container_parent.js';
window.initgui = async function(){
let url = new URL(window.location);
@@ -76,6 +77,13 @@ window.initgui = async function(){
const url_paths = window.location.pathname.split('/').filter(element => element);
if(url_paths[0]?.toLocaleLowerCase() === 'app' && url_paths[1]){
window.app_launched_from_url = url_paths[1];
+
+ // get query params, any param that doesn't start with 'puter.' will be passed to the app
+ window.app_query_params = {};
+ for (let [key, value] of url_query_params) {
+ if(!key.startsWith('puter.'))
+ app_query_params[key] = value;
+ }
}
//--------------------------------------------------------------------------------------
@@ -1976,4 +1984,32 @@ function requestOpenerOrigin() {
$(document).on('click', '.generic-close-window-button', function(e){
$(this).closest('.window').close();
+});
+
+// Re-calculate desktop height and width on window resize and re-position the login and signup windows
+$(window).on("resize", function () {
+ // If host env is popup, don't continue because the popup window has its own resize requirements.
+ if (window.embedded_in_popup)
+ return;
+
+ const ratio = window.desktop_width / window.innerWidth;
+
+ window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
+ window.desktop_width = window.innerWidth;
+
+ // Re-center the login window
+ const top = $(".window-login").position()?.top;
+ const width = $(".window-login").width();
+ $(".window-login").css({
+ left: (window.desktop_width - width) / 2,
+ top: top / ratio,
+ });
+
+ // Re-center the create account window
+ const top2 = $(".window-signup").position()?.top;
+ const width2 = $(".window-signup").width();
+ $(".window-signup").css({
+ left: (window.desktop_width - width2) / 2,
+ top: top2 / ratio,
+ });
});
\ No newline at end of file