From 39e73b3f4d5d76a1dfc8fb0a90e7fcba0c0c5ed7 Mon Sep 17 00:00:00 2001
From: ayamoosa
Date: Wed, 13 Mar 2024 08:13:52 -0700
Subject: [PATCH 01/24] added menu-aim plugin
---
src/lib/jquery.menu-aim.js | 322 +++++++++++++++++++++++++++++++++++++
src/static-assets.js | 1 +
2 files changed, 323 insertions(+)
create mode 100644 src/lib/jquery.menu-aim.js
diff --git a/src/lib/jquery.menu-aim.js b/src/lib/jquery.menu-aim.js
new file mode 100644
index 000000000..1039f5c13
--- /dev/null
+++ b/src/lib/jquery.menu-aim.js
@@ -0,0 +1,322 @@
+/**
+ * menu-aim is a jQuery plugin for dropdown menus that can differentiate
+ * between a user trying hover over a dropdown item vs trying to navigate into
+ * a submenu's contents.
+ *
+ * menu-aim assumes that you have are using a menu with submenus that expand
+ * to the menu's right. It will fire events when the user's mouse enters a new
+ * dropdown item *and* when that item is being intentionally hovered over.
+ *
+ * __________________________
+ * | Monkeys >| Gorilla |
+ * | Gorillas >| Content |
+ * | Chimps >| Here |
+ * |___________|____________|
+ *
+ * In the above example, "Gorillas" is selected and its submenu content is
+ * being shown on the right. Imagine that the user's cursor is hovering over
+ * "Gorillas." When they move their mouse into the "Gorilla Content" area, they
+ * may briefly hover over "Chimps." This shouldn't close the "Gorilla Content"
+ * area.
+ *
+ * This problem is normally solved using timeouts and delays. menu-aim tries to
+ * solve this by detecting the direction of the user's mouse movement. This can
+ * make for quicker transitions when navigating up and down the menu. The
+ * experience is hopefully similar to amazon.com/'s "Shop by Department"
+ * dropdown.
+ *
+ * Use like so:
+ *
+ * $("#menu").menuAim({
+ * activate: $.noop, // fired on row activation
+ * deactivate: $.noop // fired on row deactivation
+ * });
+ *
+ * ...to receive events when a menu's row has been purposefully (de)activated.
+ *
+ * The following options can be passed to menuAim. All functions execute with
+ * the relevant row's HTML element as the execution context ('this'):
+ *
+ * .menuAim({
+ * // Function to call when a row is purposefully activated. Use this
+ * // to show a submenu's content for the activated row.
+ * activate: function() {},
+ *
+ * // Function to call when a row is deactivated.
+ * deactivate: function() {},
+ *
+ * // Function to call when mouse enters a menu row. Entering a row
+ * // does not mean the row has been activated, as the user may be
+ * // mousing over to a submenu.
+ * enter: function() {},
+ *
+ * // Function to call when mouse exits a menu row.
+ * exit: function() {},
+ *
+ * // Selector for identifying which elements in the menu are rows
+ * // that can trigger the above events. Defaults to "> li".
+ * rowSelector: "> li",
+ *
+ * // You may have some menu rows that aren't submenus and therefore
+ * // shouldn't ever need to "activate." If so, filter submenu rows w/
+ * // this selector. Defaults to "*" (all elements).
+ * submenuSelector: "*",
+ *
+ * // Direction the submenu opens relative to the main menu. Can be
+ * // left, right, above, or below. Defaults to "right".
+ * submenuDirection: "right"
+ * });
+ *
+ * https://github.com/kamens/jQuery-menu-aim
+*/
+(function($) {
+
+ $.fn.menuAim = function(opts) {
+ // Initialize menu-aim for all elements in jQuery collection
+ this.each(function() {
+ init.call(this, opts);
+ });
+
+ return this;
+ };
+
+ function init(opts) {
+ var $menu = $(this),
+ activeRow = null,
+ mouseLocs = [],
+ lastDelayLoc = null,
+ timeoutId = null,
+ options = $.extend({
+ rowSelector: "> li",
+ submenuSelector: "*",
+ submenuDirection: "right",
+ tolerance: 75, // bigger = more forgivey when entering submenu
+ enter: $.noop,
+ exit: $.noop,
+ activate: $.noop,
+ deactivate: $.noop,
+ exitMenu: $.noop
+ }, opts);
+
+ var MOUSE_LOCS_TRACKED = 3, // number of past mouse locations to track
+ DELAY = 300; // ms delay when user appears to be entering submenu
+
+ /**
+ * Keep track of the last few locations of the mouse.
+ */
+ var mousemoveDocument = function(e) {
+ mouseLocs.push({x: e.pageX, y: e.pageY});
+
+ if (mouseLocs.length > MOUSE_LOCS_TRACKED) {
+ mouseLocs.shift();
+ }
+ };
+
+ /**
+ * Cancel possible row activations when leaving the menu entirely
+ */
+ var mouseleaveMenu = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+
+ // If exitMenu is supplied and returns true, deactivate the
+ // currently active row on menu exit.
+ if (options.exitMenu(this)) {
+ if (activeRow) {
+ options.deactivate(activeRow);
+ }
+
+ activeRow = null;
+ }
+ };
+
+ /**
+ * Trigger a possible row activation whenever entering a new row.
+ */
+ var mouseenterRow = function() {
+ if (timeoutId) {
+ // Cancel any previous activation delays
+ clearTimeout(timeoutId);
+ }
+
+ options.enter(this);
+ possiblyActivate(this);
+ },
+ mouseleaveRow = function() {
+ options.exit(this);
+ };
+
+ /*
+ * Immediately activate a row if the user clicks on it.
+ */
+ var clickRow = function() {
+ activate(this);
+ };
+
+ /**
+ * Activate a menu row.
+ */
+ var activate = function(row) {
+ if (row == activeRow) {
+ return;
+ }
+
+ if (activeRow) {
+ options.deactivate(activeRow);
+ }
+
+ options.activate(row);
+ activeRow = row;
+ };
+
+ /**
+ * Possibly activate a menu row. If mouse movement indicates that we
+ * shouldn't activate yet because user may be trying to enter
+ * a submenu's content, then delay and check again later.
+ */
+ var possiblyActivate = function(row) {
+ var delay = activationDelay();
+
+ if (delay) {
+ timeoutId = setTimeout(function() {
+ possiblyActivate(row);
+ }, delay);
+ } else {
+ activate(row);
+ }
+ };
+
+ /**
+ * Return the amount of time that should be used as a delay before the
+ * currently hovered row is activated.
+ *
+ * Returns 0 if the activation should happen immediately. Otherwise,
+ * returns the number of milliseconds that should be delayed before
+ * checking again to see if the row should be activated.
+ */
+ var activationDelay = function() {
+ if (!activeRow || !$(activeRow).is(options.submenuSelector)) {
+ // If there is no other submenu row already active, then
+ // go ahead and activate immediately.
+ return 0;
+ }
+
+ var offset = $menu.offset(),
+ upperLeft = {
+ x: offset.left,
+ y: offset.top - options.tolerance
+ },
+ upperRight = {
+ x: offset.left + $menu.outerWidth(),
+ y: upperLeft.y
+ },
+ lowerLeft = {
+ x: offset.left,
+ y: offset.top + $menu.outerHeight() + options.tolerance
+ },
+ lowerRight = {
+ x: offset.left + $menu.outerWidth(),
+ y: lowerLeft.y
+ },
+ loc = mouseLocs[mouseLocs.length - 1],
+ prevLoc = mouseLocs[0];
+
+ if (!loc) {
+ return 0;
+ }
+
+ if (!prevLoc) {
+ prevLoc = loc;
+ }
+
+ if (prevLoc.x < offset.left || prevLoc.x > lowerRight.x ||
+ prevLoc.y < offset.top || prevLoc.y > lowerRight.y) {
+ // If the previous mouse location was outside of the entire
+ // menu's bounds, immediately activate.
+ return 0;
+ }
+
+ if (lastDelayLoc &&
+ loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) {
+ // If the mouse hasn't moved since the last time we checked
+ // for activation status, immediately activate.
+ return 0;
+ }
+
+ // Detect if the user is moving towards the currently activated
+ // submenu.
+ //
+ // If the mouse is heading relatively clearly towards
+ // the submenu's content, we should wait and give the user more
+ // time before activating a new row. If the mouse is heading
+ // elsewhere, we can immediately activate a new row.
+ //
+ // We detect this by calculating the slope formed between the
+ // current mouse location and the upper/lower right points of
+ // the menu. We do the same for the previous mouse location.
+ // If the current mouse location's slopes are
+ // increasing/decreasing appropriately compared to the
+ // previous's, we know the user is moving toward the submenu.
+ //
+ // Note that since the y-axis increases as the cursor moves
+ // down the screen, we are looking for the slope between the
+ // cursor and the upper right corner to decrease over time, not
+ // increase (somewhat counterintuitively).
+ function slope(a, b) {
+ return (b.y - a.y) / (b.x - a.x);
+ };
+
+ var decreasingCorner = upperRight,
+ increasingCorner = lowerRight;
+
+ // Our expectations for decreasing or increasing slope values
+ // depends on which direction the submenu opens relative to the
+ // main menu. By default, if the menu opens on the right, we
+ // expect the slope between the cursor and the upper right
+ // corner to decrease over time, as explained above. If the
+ // submenu opens in a different direction, we change our slope
+ // expectations.
+ if (options.submenuDirection == "left") {
+ decreasingCorner = lowerLeft;
+ increasingCorner = upperLeft;
+ } else if (options.submenuDirection == "below") {
+ decreasingCorner = lowerRight;
+ increasingCorner = lowerLeft;
+ } else if (options.submenuDirection == "above") {
+ decreasingCorner = upperLeft;
+ increasingCorner = upperRight;
+ }
+
+ var decreasingSlope = slope(loc, decreasingCorner),
+ increasingSlope = slope(loc, increasingCorner),
+ prevDecreasingSlope = slope(prevLoc, decreasingCorner),
+ prevIncreasingSlope = slope(prevLoc, increasingCorner);
+
+ if (decreasingSlope < prevDecreasingSlope &&
+ increasingSlope > prevIncreasingSlope) {
+ // Mouse is moving from previous location towards the
+ // currently activated submenu. Delay before activating a
+ // new menu row, because user may be moving into submenu.
+ lastDelayLoc = loc;
+ return DELAY;
+ }
+
+ lastDelayLoc = null;
+ return 0;
+ };
+
+ /**
+ * Hook up initial menu events
+ */
+ $menu
+ .mouseleave(mouseleaveMenu)
+ .find(options.rowSelector)
+ .mouseenter(mouseenterRow)
+ .mouseleave(mouseleaveRow)
+ .click(clickRow);
+
+ $(document).mousemove(mousemoveDocument);
+
+ };
+})(jQuery);
\ No newline at end of file
diff --git a/src/static-assets.js b/src/static-assets.js
index 32e5cf7ee..da8d50a02 100644
--- a/src/static-assets.js
+++ b/src/static-assets.js
@@ -27,6 +27,7 @@ const lib_paths =[
`/lib/jquery-ui-1.13.2/jquery-ui.min.js`,
`/lib/lodash@4.17.21.min.js`,
`/lib/jquery.dragster.js`,
+ '/lib/jquery.menu-aim.js',
`/lib/html-entities.js`,
`/lib/timeago.min.js`,
`/lib/iro.min.js`,
From 99ab5cec5b59876e99eae1dc3f4aaa93942df80b Mon Sep 17 00:00:00 2001
From: ayamoosa
Date: Wed, 13 Mar 2024 09:21:18 -0700
Subject: [PATCH 02/24] works on right side
---
src/UI/UIContextMenu.js | 119 +++++++++++++++++++++++++++----------
src/lib/jquery.menu-aim.js | 2 +-
2 files changed, 89 insertions(+), 32 deletions(-)
diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js
index a6168eda4..bb86aa230 100644
--- a/src/UI/UIContextMenu.js
+++ b/src/UI/UIContextMenu.js
@@ -149,42 +149,99 @@ function UIContextMenu(options){
return false;
});
- // when mouse is over an item
- $(contextMenu).find('.context-menu-item').on('mouseover', function (e) {
- // mark other items as inactive
- $(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
- // mark this item as active
- $(this).addClass('context-menu-item-active');
- // close any submenu that doesn't belong to this item
- $(`.context-menu[data-parent-id="${menu_id}"]`).remove();
- // mark this context menu as active
- $(contextMenu).addClass('context-menu-active');
- })
+ $(contextMenu).menuAim({
+ submenuDirection: function (e){
+ // if submenu will open to the left of menu item
+ if (e.getBoundingClientRect().left + e.getBoundingClientRect().width > window.innerWidth) {
+ return 'left';
+ } else {
+ return 'right';
+ }
+ },
+ activate: function (e) {
+
+ console.log('activate', e)
+
+ // mark other items as inactive
+ $(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
+ // mark this item as active
+ $(e).addClass('context-menu-item-active');
+ // close any submenu that doesn't belong to this item
+ $(`.context-menu[data-parent-id="${menu_id}"]`).remove();
+ // mark this context menu as active
+ $(contextMenu).addClass('context-menu-active');
+
+
+ // activate submenu
+ // open submenu if applicable
+ if($(e).hasClass('context-menu-item-submenu')){
+ let item_rect_box = e.getBoundingClientRect();
+ // open submenu only if it's not already open
+ if($(`.context-menu[data-id="${menu_id}-${$(e).attr('data-action')}"]`).length === 0){
+ // close other submenus
+ $(`.context-menu[parent-element-id="${menu_id}"]`).remove();
+ // open the new submenu
+ UIContextMenu({
+ items: options.items[parseInt($(e).attr('data-action'))].items,
+ parent_id: menu_id,
+ is_submenu: true,
+ id: menu_id + '-' + $(e).attr('data-action'),
+ position:{
+ top: item_rect_box.top - 5,
+ left: x_pos + item_rect_box.width + 15,
+ }
+ })
+ }
+ }
+ },
+ deactivate: function (e) {
+ console.log('deactivate')
+ //deactivate submenu
+ if($(e).hasClass('context-menu-item-submenu')){
+ $(`.context-menu[data-id="${menu_id}-${$(e).attr('data-action')}"]`).remove();
+ }
+ }
+ });
+
+
+
+
+ // // when mouse is over an item
+ // $(contextMenu).find('.context-menu-item').on('mouseover', function (e) {
+ // // mark other items as inactive
+ // $(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
+ // // mark this item as active
+ // $(this).addClass('context-menu-item-active');
+ // // close any submenu that doesn't belong to this item
+ // $(`.context-menu[data-parent-id="${menu_id}"]`).remove();
+ // // mark this context menu as active
+ // $(contextMenu).addClass('context-menu-active');
+ // })
// open submenu if applicable
- $(`#context-menu-${menu_id} > li.context-menu-item-submenu`).on('mouseover', function (e) {
+ // $(`#context-menu-${menu_id} > li.context-menu-item-submenu`).on('mouseover', function (e) {
- // open submenu only if it's not already open
- if($(`.context-menu[data-id="${menu_id}-${$(this).attr('data-action')}"]`).length === 0){
- let item_rect_box = this.getBoundingClientRect();
+ // // open submenu only if it's not already open
+ // if($(`.context-menu[data-id="${menu_id}-${$(this).attr('data-action')}"]`).length === 0){
+ // let item_rect_box = this.getBoundingClientRect();
- // close other submenus
- $(`.context-menu[parent-element-id="${menu_id}"]`).remove();
+ // // close other submenus
+ // $(`.context-menu[parent-element-id="${menu_id}"]`).remove();
- // open the new submenu
- UIContextMenu({
- items: options.items[parseInt($(this).attr('data-action'))].items,
- parent_id: menu_id,
- is_submenu: true,
- id: menu_id + '-' + $(this).attr('data-action'),
- position:{
- top: item_rect_box.top - 5,
- left: x_pos + item_rect_box.width + 15,
- }
- })
- }
- return false;
- });
+ // // open the new submenu
+ // UIContextMenu({
+ // items: options.items[parseInt($(this).attr('data-action'))].items,
+ // parent_id: menu_id,
+ // is_submenu: true,
+ // id: menu_id + '-' + $(this).attr('data-action'),
+ // position:{
+ // top: item_rect_box.top - 5,
+ // left: x_pos + item_rect_box.width + 15,
+ // }
+ // })
+ // }
+ // return false;
+ // });
// useful in cases such as where a menue item is over a window, this prevents from the mousedown event
// reaching the window underneath
diff --git a/src/lib/jquery.menu-aim.js b/src/lib/jquery.menu-aim.js
index 1039f5c13..0c2f82bad 100644
--- a/src/lib/jquery.menu-aim.js
+++ b/src/lib/jquery.menu-aim.js
@@ -89,7 +89,7 @@
options = $.extend({
rowSelector: "> li",
submenuSelector: "*",
- submenuDirection: "right",
+ submenuDirection: $.noop,
tolerance: 75, // bigger = more forgivey when entering submenu
enter: $.noop,
exit: $.noop,
From 59f4877ee551e1c57c13b087c32cbef44c93fa67 Mon Sep 17 00:00:00 2001
From: Axorax <78349410+Axorax@users.noreply.github.com>
Date: Thu, 14 Mar 2024 12:23:53 +0600
Subject: [PATCH 03/24] update: use any port if all attempts fail
---
dev-server.js | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/dev-server.js b/dev-server.js
index 30f149d1d..2add59668 100644
--- a/dev-server.js
+++ b/dev-server.js
@@ -10,15 +10,14 @@ let port = process.env.PORT ?? 4000; // Starting port
const maxAttempts = 10; // Maximum number of ports to try
const env = argv[2] ?? "dev";
-const startServer = (attempt) => {
+const startServer = (attempt, useAnyFreePort = false) => {
if (attempt > maxAttempts) {
- console.error(chalk.red(`ERROR: Unable to find an available port after ${maxAttempts} attempts.`));
- return;
+ useAnyFreePort = true; // Use any port that is free
}
- app.listen(port, () => {
+ const server = app.listen(useAnyFreePort ? 0 : port, () => {
console.log("\n-----------------------------------------------------------\n");
- console.log(`Puter is now live at: `, chalk.underline.blue(`http://localhost:${port}`));
+ console.log(`Puter is now live at: `, chalk.underline.blue(`http://localhost:${server.address().port}`));
console.log("\n-----------------------------------------------------------\n");
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') { // Check if the error is because the port is already in use
From d5ef139db086d2d08e4d6a21670a42be6077bcda Mon Sep 17 00:00:00 2001
From: KernelDeimos
Date: Thu, 14 Mar 2024 14:15:10 -0400
Subject: [PATCH 04/24] Add checklists for x86 emulation project
---
incubator/x86emu/README.md | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 incubator/x86emu/README.md
diff --git a/incubator/x86emu/README.md b/incubator/x86emu/README.md
new file mode 100644
index 000000000..e74e444de
--- /dev/null
+++ b/incubator/x86emu/README.md
@@ -0,0 +1,17 @@
+# Research + Planning for x86 Emulation in Puter
+
+## Resources
+- [copy.sh/v86 docs](https://github.com/copy/v86/blob/master/docs)
+- [greenfield github](https://github.com/udevbe/greenfield)
+
+## TODO
+
+### Documents to Write
+
+- [ ] specification for Puter network driver
+- [ ] specification for Puter network relay
+
+### Things to Try
+
+- [ ] greenfield/wayland/arch/v86
+- [ ] puter-fuse in v86
From 072eec4004edb7a5778b087d8828082c03c0b6a1 Mon Sep 17 00:00:00 2001
From: Sylvain Huguet
Date: Fri, 15 Mar 2024 00:15:16 +0100
Subject: [PATCH 05/24] Initial version
---
.github/workflows/docker-image.yaml | 74 +++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100644 .github/workflows/docker-image.yaml
diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
new file mode 100644
index 000000000..4490f0a27
--- /dev/null
+++ b/.github/workflows/docker-image.yaml
@@ -0,0 +1,74 @@
+#
+name: Docker Image CI
+
+# Configures this workflow to run every time a change is pushed to the
+# branch called `master`.
+on:
+ push:
+ branches: ['master']
+ pull_request:
+ branches: ['master']
+ schedule:
+ - cron: "0 2 * * 1-5"
+
+
+# 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 }}
+
+ # 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 }}
From c7850fe18f1d45010c5f0dc9f4f2b0e1ccc35706 Mon Sep 17 00:00:00 2001
From: Sylvain Huguet
Date: Fri, 15 Mar 2024 00:40:39 +0100
Subject: [PATCH 06/24] Tweaking the metadata/image tagging mechanism
---
.github/workflows/docker-image.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
index 4490f0a27..60e0c52ef 100644
--- a/.github/workflows/docker-image.yaml
+++ b/.github/workflows/docker-image.yaml
@@ -55,6 +55,15 @@ jobs:
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,
From cc9d9b10635978e76f237c4596b5f898081a2216 Mon Sep 17 00:00:00 2001
From: Sylvain Huguet
Date: Fri, 15 Mar 2024 00:43:02 +0100
Subject: [PATCH 07/24] fix indentation
---
.github/workflows/docker-image.yaml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
index 60e0c52ef..0abd22641 100644
--- a/.github/workflows/docker-image.yaml
+++ b/.github/workflows/docker-image.yaml
@@ -56,14 +56,14 @@ jobs:
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) }}
+ 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,
From 06789da119dc4b26f8f0a000804b6ada7c27dcc0 Mon Sep 17 00:00:00 2001
From: Sylvain Huguet
Date: Fri, 15 Mar 2024 10:42:22 +0100
Subject: [PATCH 08/24] Renamed `master` branch to `main` to reflect upstream
changes.
---
.github/workflows/docker-image.yaml | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
index 0abd22641..67c17c2ac 100644
--- a/.github/workflows/docker-image.yaml
+++ b/.github/workflows/docker-image.yaml
@@ -5,11 +5,7 @@ name: Docker Image CI
# branch called `master`.
on:
push:
- branches: ['master']
- pull_request:
- branches: ['master']
- schedule:
- - cron: "0 2 * * 1-5"
+ branches: ['main']
# Defines two custom environment variables for the workflow. These are used
From f6279d020108301424b89852fb08a0a5178e09c8 Mon Sep 17 00:00:00 2001
From: Sylvain Huguet
Date: Fri, 15 Mar 2024 10:51:12 +0100
Subject: [PATCH 09/24] Change branch name in comment too.
---
.github/workflows/docker-image.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml
index 67c17c2ac..ba1aa4360 100644
--- a/.github/workflows/docker-image.yaml
+++ b/.github/workflows/docker-image.yaml
@@ -2,7 +2,7 @@
name: Docker Image CI
# Configures this workflow to run every time a change is pushed to the
-# branch called `master`.
+# branch called `main`.
on:
push:
branches: ['main']
From f88cfc750f80e3f0ccca45662b23fc2bbe3d40d6 Mon Sep 17 00:00:00 2001
From: ayamoosa
Date: Fri, 15 Mar 2024 09:15:40 -0700
Subject: [PATCH 10/24] need to work on submenuDirection reactivity
---
src/UI/UIContextMenu.js | 44 ++++++++++++++++++++++++++++++--------
src/lib/jquery.menu-aim.js | 7 +++---
2 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js
index bb86aa230..c4852dcc2 100644
--- a/src/UI/UIContextMenu.js
+++ b/src/UI/UIContextMenu.js
@@ -149,23 +149,45 @@ function UIContextMenu(options){
return false;
});
+ // initialize menuAim plugin
$(contextMenu).menuAim({
- submenuDirection: function (e){
- // if submenu will open to the left of menu item
- if (e.getBoundingClientRect().left + e.getBoundingClientRect().width > window.innerWidth) {
- return 'left';
- } else {
- return 'right';
- }
+ submenuDirection: function(){
+ //if this is not a submenu
+ // if(!options.is_submenu){
+ // if submenu is going to be on the right of the main menu
+ if(x_pos + $(contextMenu).width() + 10 > window.innerWidth){
+ console.log('right')
+ return "right";
+ } else {
+ console.log('left')
+ return "left";
+ }
+ // }
},
activate: function (e) {
+
+ //if submenu opens on right make submenuDirection right
+ // if($(contextMenu).offset().left + $(contextMenu).width() + 10 > window.innerWidth){
+ // $(contextMenu).menuAim({
+ // submenuDirection: 'right'
+ // });
+ // } else {
+ // $(contextMenu).menuAim({
+ // submenuDirection: 'left'
+ // });
+ // }
+ // hover over an item
+
+
+
+ let item = $(e).closest('.context-menu-item');
console.log('activate', e)
// mark other items as inactive
$(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
// mark this item as active
- $(e).addClass('context-menu-item-active');
+ $(item).addClass('context-menu-item-active');
// close any submenu that doesn't belong to this item
$(`.context-menu[data-parent-id="${menu_id}"]`).remove();
// mark this context menu as active
@@ -173,6 +195,7 @@ function UIContextMenu(options){
// activate submenu
+
// open submenu if applicable
if($(e).hasClass('context-menu-item-submenu')){
let item_rect_box = e.getBoundingClientRect();
@@ -193,6 +216,7 @@ function UIContextMenu(options){
})
}
}
+
},
deactivate: function (e) {
console.log('deactivate')
@@ -284,4 +308,6 @@ window.select_ctxmenu_item = function ($ctxmenu_item){
$($ctxmenu_item).addClass('context-menu-item-active');
}
-export default UIContextMenu;
\ No newline at end of file
+export default UIContextMenu;
+
+
diff --git a/src/lib/jquery.menu-aim.js b/src/lib/jquery.menu-aim.js
index 0c2f82bad..471053400 100644
--- a/src/lib/jquery.menu-aim.js
+++ b/src/lib/jquery.menu-aim.js
@@ -166,6 +166,7 @@
options.deactivate(activeRow);
}
+
options.activate(row);
activeRow = row;
};
@@ -277,13 +278,13 @@
// corner to decrease over time, as explained above. If the
// submenu opens in a different direction, we change our slope
// expectations.
- if (options.submenuDirection == "left") {
+ if (options.submenuDirection() == "left") {
decreasingCorner = lowerLeft;
increasingCorner = upperLeft;
- } else if (options.submenuDirection == "below") {
+ } else if (options.submenuDirection() == "below") {
decreasingCorner = lowerRight;
increasingCorner = lowerLeft;
- } else if (options.submenuDirection == "above") {
+ } else if (options.submenuDirection() == "above") {
decreasingCorner = upperLeft;
increasingCorner = upperRight;
}
From 8ec0e63d7b3ffca72b346e06c5b247896766e8ff Mon Sep 17 00:00:00 2001
From: ayamoosa
Date: Fri, 15 Mar 2024 12:31:40 -0700
Subject: [PATCH 11/24] fixed left/right issue. Feature is ready
---
src/UI/UIContextMenu.js | 78 ++++++-----------------------------------
1 file changed, 10 insertions(+), 68 deletions(-)
diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js
index c4852dcc2..ce0ab8f80 100644
--- a/src/UI/UIContextMenu.js
+++ b/src/UI/UIContextMenu.js
@@ -145,45 +145,28 @@ function UIContextMenu(options){
$(contextMenu).remove();
});
}
-
return false;
});
- // initialize menuAim plugin
+ // initialize menuAim plugin (../libs/jquery.menu-aim.js)
$(contextMenu).menuAim({
submenuDirection: function(){
- //if this is not a submenu
- // if(!options.is_submenu){
- // if submenu is going to be on the right of the main menu
- if(x_pos + $(contextMenu).width() + 10 > window.innerWidth){
- console.log('right')
+ //if not submenu
+ if(!options.is_submenu){
+ // if submenu left postiton is greater than main menu left position
+ if($(contextMenu).offset().left + 2 * $(contextMenu).width() + 15 < window.innerWidth ){
return "right";
} else {
- console.log('left')
return "left";
}
- // }
+ }
},
+ //activates item when mouse enters depending in mouse position and direction
activate: function (e) {
-
- //if submenu opens on right make submenuDirection right
- // if($(contextMenu).offset().left + $(contextMenu).width() + 10 > window.innerWidth){
- // $(contextMenu).menuAim({
- // submenuDirection: 'right'
- // });
- // } else {
- // $(contextMenu).menuAim({
- // submenuDirection: 'left'
- // });
- // }
- // hover over an item
-
-
+ //activate items
let item = $(e).closest('.context-menu-item');
- console.log('activate', e)
-
// mark other items as inactive
$(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
// mark this item as active
@@ -195,7 +178,6 @@ function UIContextMenu(options){
// activate submenu
-
// open submenu if applicable
if($(e).hasClass('context-menu-item-submenu')){
let item_rect_box = e.getBoundingClientRect();
@@ -216,8 +198,8 @@ function UIContextMenu(options){
})
}
}
-
},
+ //deactivates row when mouse leavess
deactivate: function (e) {
console.log('deactivate')
//deactivate submenu
@@ -227,47 +209,7 @@ function UIContextMenu(options){
}
});
-
-
-
- // // when mouse is over an item
- // $(contextMenu).find('.context-menu-item').on('mouseover', function (e) {
- // // mark other items as inactive
- // $(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
- // // mark this item as active
- // $(this).addClass('context-menu-item-active');
- // // close any submenu that doesn't belong to this item
- // $(`.context-menu[data-parent-id="${menu_id}"]`).remove();
- // // mark this context menu as active
- // $(contextMenu).addClass('context-menu-active');
- // })
-
- // open submenu if applicable
- // $(`#context-menu-${menu_id} > li.context-menu-item-submenu`).on('mouseover', function (e) {
-
- // // open submenu only if it's not already open
- // if($(`.context-menu[data-id="${menu_id}-${$(this).attr('data-action')}"]`).length === 0){
- // let item_rect_box = this.getBoundingClientRect();
-
- // // close other submenus
- // $(`.context-menu[parent-element-id="${menu_id}"]`).remove();
-
- // // open the new submenu
- // UIContextMenu({
- // items: options.items[parseInt($(this).attr('data-action'))].items,
- // parent_id: menu_id,
- // is_submenu: true,
- // id: menu_id + '-' + $(this).attr('data-action'),
- // position:{
- // top: item_rect_box.top - 5,
- // left: x_pos + item_rect_box.width + 15,
- // }
- // })
- // }
- // return false;
- // });
-
- // useful in cases such as where a menue item is over a window, this prevents from the mousedown event
+ // useful in cases such as where a menu item is over a window, this prevents from the mousedown event
// reaching the window underneath
$(`#context-menu-${menu_id} > li:not(.context-menu-item-disabled)`).on('mousedown', function (e) {
e.preventDefault();
From a5584317c8d001d6e47f2e45798be8ce7183a155 Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Fri, 15 Mar 2024 16:53:05 -0700
Subject: [PATCH 12/24] Allow apps to resize/reposition their windows
The resizing and repositioning is safe from abuse in that the window's position and size cannot cause it to escape the viewport. More could be done here, e.g. rate limit resize/repos. I will request rate-limiting in a separate issue.
---
src/IPC.js | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 92 insertions(+), 1 deletion(-)
diff --git a/src/IPC.js b/src/IPC.js
index 18335d90d..5520ee006 100644
--- a/src/IPC.js
+++ b/src/IPC.js
@@ -333,7 +333,6 @@ window.addEventListener('message', async (event) => {
initiating_app_uuid: app_uuid,
});
}
-
//--------------------------------------------------------
// setWindowTitle
//--------------------------------------------------------
@@ -347,6 +346,98 @@ window.addEventListener('message', async (event) => {
}, '*');
}
//--------------------------------------------------------
+ // setWindowWidth
+ //--------------------------------------------------------
+ else if(event.data.msg === 'setWindowWidth' && event.data.width !== undefined){
+ event.data.width = parseFloat(event.data.width);
+ // must be at least 200
+ if(event.data.width < 200)
+ event.data.width = 200;
+ // set window width
+ $($el_parent_window).css('width', event.data.width);
+ // send confirmation to requester window
+ target_iframe.contentWindow.postMessage({
+ original_msg_id: msg_id,
+ }, '*');
+ }
+ //--------------------------------------------------------
+ // setWindowHeight
+ //--------------------------------------------------------
+ else if(event.data.msg === 'setWindowHeight' && event.data.height !== undefined){
+ event.data.height = parseFloat(event.data.height);
+ // must be at least 200
+ if(event.data.height < 200)
+ event.data.height = 200;
+
+ // convert to number and set
+ $($el_parent_window).css('height', event.data.height);
+
+ // send confirmation to requester window
+ target_iframe.contentWindow.postMessage({
+ original_msg_id: msg_id,
+ }, '*');
+ }
+ //--------------------------------------------------------
+ // setWindowSize
+ //--------------------------------------------------------
+ else if(event.data.msg === 'setWindowSize' && (event.data.width !== undefined || event.data.height !== undefined)){
+ // convert to number and set
+ if(event.data.width !== undefined){
+ event.data.width = parseFloat(event.data.width);
+ // must be at least 200
+ if(event.data.width < 200)
+ event.data.width = 200;
+ $($el_parent_window).css('width', event.data.width);
+ }
+
+ if(event.data.height !== undefined){
+ event.data.height = parseFloat(event.data.height);
+ // must be at least 200
+ if(event.data.height < 200)
+ event.data.height = 200;
+ $($el_parent_window).css('height', event.data.height);
+ }
+
+ // send confirmation to requester window
+ target_iframe.contentWindow.postMessage({
+ original_msg_id: msg_id,
+ }, '*');
+ }
+ //--------------------------------------------------------
+ // setWindowPosition
+ //--------------------------------------------------------
+ else if(event.data.msg === 'setWindowPosition' && (event.data.x !== undefined || event.data.y !== undefined)){
+ // convert to number and set
+ if(event.data.x !== undefined){
+ event.data.x = parseFloat(event.data.x);
+ // we don't want the window to go off the left edge of the screen
+ if(event.data.x < 0)
+ event.data.x = 0;
+ // we don't want the window to go off the right edge of the screen
+ if(event.data.x > window.innerWidth - 100)
+ event.data.x = window.innerWidth - 100;
+ // set window left
+ $($el_parent_window).css('left', parseFloat(event.data.x));
+ }
+
+ if(event.data.y !== undefined){
+ event.data.y = parseFloat(event.data.y);
+ // we don't want the window to go off the top edge of the screen
+ if(event.data.y < window.taskbar_height)
+ event.data.y = window.taskbar_height;
+ // we don't want the window to go off the bottom edge of the screen
+ if(event.data.y > window.innerHeight - 100)
+ event.data.y = window.innerHeight - 100;
+ // set window top
+ $($el_parent_window).css('top', parseFloat(event.data.y));
+ }
+
+ // send confirmation to requester window
+ target_iframe.contentWindow.postMessage({
+ original_msg_id: msg_id,
+ }, '*');
+ }
+ //--------------------------------------------------------
// watchItem
//--------------------------------------------------------
else if(event.data.msg === 'watchItem' && event.data.item_uid !== undefined){
From 4719d7e07676efab5ce8c024e1e76ac2a6cf84eb Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Fri, 15 Mar 2024 17:16:54 -0700
Subject: [PATCH 13/24] Update UIContextMenu.js
---
src/UI/UIContextMenu.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js
index ce0ab8f80..20491ed3f 100644
--- a/src/UI/UIContextMenu.js
+++ b/src/UI/UIContextMenu.js
@@ -201,7 +201,6 @@ function UIContextMenu(options){
},
//deactivates row when mouse leavess
deactivate: function (e) {
- console.log('deactivate')
//deactivate submenu
if($(e).hasClass('context-menu-item-submenu')){
$(`.context-menu[data-id="${menu_id}-${$(e).attr('data-action')}"]`).remove();
From 06b075b1a44b75f2060ae75abc1b403605caade5 Mon Sep 17 00:00:00 2001
From: meetqy
Date: Sat, 16 Mar 2024 13:48:11 +0800
Subject: [PATCH 14/24] fix: #90
---
src/globals.js | 132 +++++++++++++++++++++++++++----------------------
1 file changed, 74 insertions(+), 58 deletions(-)
diff --git a/src/globals.js b/src/globals.js
index f5ff998ad..222e779ce 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -7,17 +7,17 @@
* 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 .
*/
-window.clipboard_op = '';
+window.clipboard_op = "";
window.clipboard = [];
window.actions_history = [];
window.window_nav_history = {};
@@ -43,63 +43,65 @@ window.mouseX = 0;
window.mouseY = 0;
// get all logged-in users
-try{
- window.logged_in_users = JSON.parse(localStorage.getItem("logged_in_users"));
-}catch(e){
- window.logged_in_users = [];
+try {
+ window.logged_in_users = JSON.parse(localStorage.getItem("logged_in_users"));
+} catch (e) {
+ window.logged_in_users = [];
}
-if(window.logged_in_users === null)
- window.logged_in_users = [];
+if (window.logged_in_users === null) window.logged_in_users = [];
// this sessions's user
window.auth_token = localStorage.getItem("auth_token");
-try{
- window.user = JSON.parse(localStorage.getItem("user"));
-}catch(e){
- window.user = null;
+try {
+ window.user = JSON.parse(localStorage.getItem("user"));
+} catch (e) {
+ window.user = null;
}
// in case this is the first time user is visiting multi-user feature
-if(window.logged_in_users.length === 0 && window.user !== null){
- let tuser = window.user;
- tuser.auth_token = window.auth_token
- window.logged_in_users.push(tuser);
- localStorage.setItem("logged_in_users", window.logged_in_users);
+if (window.logged_in_users.length === 0 && window.user !== null) {
+ let tuser = window.user;
+ tuser.auth_token = window.auth_token;
+ window.logged_in_users.push(tuser);
+ localStorage.setItem("logged_in_users", window.logged_in_users);
}
window.last_window_zindex = 1;
// first visit tracker
-window.first_visit_ever = localStorage.getItem("has_visited_before") === null ? true : false;
+window.first_visit_ever =
+ localStorage.getItem("has_visited_before") === null ? true : false;
localStorage.setItem("has_visited_before", true);
// system paths
-if(window.user !== undefined && window.user !== null){
- window.desktop_path = '/' + window.user.username + '/Desktop';
- window.trash_path = '/' + window.user.username + '/Trash';
- window.appdata_path = '/' + window.user.username + '/AppData';
- window.documents_path = '/' + window.user.username + '/Documents';
- window.pictures_path = '/' + window.user.username + '/Photos';
- window.videos_path = '/' + window.user.username + '/Videos';
- window.audio_path = '/' + window.user.username + '/Audio';
- window.home_path = '/' + window.user.username;
+if (window.user !== undefined && window.user !== null) {
+ window.desktop_path = "/" + window.user.username + "/Desktop";
+ window.trash_path = "/" + window.user.username + "/Trash";
+ window.appdata_path = "/" + window.user.username + "/AppData";
+ window.documents_path = "/" + window.user.username + "/Documents";
+ window.pictures_path = "/" + window.user.username + "/Photos";
+ window.videos_path = "/" + window.user.username + "/Videos";
+ window.audio_path = "/" + window.user.username + "/Audio";
+ window.home_path = "/" + window.user.username;
}
-window.root_dirname = 'Puter';
+window.root_dirname = "Puter";
// user preferences, persisted across sessions, cached in localStorage
try {
- window.user_preferences = JSON.parse(localStorage.getItem('user_preferences'))
-}catch(e){
- window.user_preferences = null;
+ window.user_preferences = JSON.parse(
+ localStorage.getItem("user_preferences")
+ );
+} catch (e) {
+ window.user_preferences = null;
}
// default values
if (window.user_preferences === null) {
- window.user_preferences = {
- show_hidden_files: false,
- }
+ window.user_preferences = {
+ show_hidden_files: false,
+ };
}
-window.window_stack = []
+window.window_stack = [];
window.toolbar_height = 30;
window.default_taskbar_height = 50;
window.taskbar_height = window.default_taskbar_height;
@@ -112,43 +114,57 @@ window.operation_id = 0;
window.operation_cancelled = [];
window.last_enter_pressed_to_rename_ts = 0;
window.window_counter = 0;
-window.keypress_item_seach_term = '';
+window.keypress_item_seach_term = "";
window.keypress_item_seach_buffer_timeout = undefined;
window.first_visit_animation = false;
window.show_twitter_link = true;
window.animate_window_opening = true;
window.animate_window_closing = true;
-window.desktop_loading_fade_delay = (window.first_visit_ever && first_visit_animation ? 6000 : 1000);
+window.desktop_loading_fade_delay =
+ window.first_visit_ever && first_visit_animation ? 6000 : 1000;
window.watchItems = [];
window.appdata_signatures = {};
window.appCallbackFunctions = [];
// 'Launch' apps
window.launch_apps = [];
-window.launch_apps.recent = []
-window.launch_apps.recommended = []
+window.launch_apps.recent = [];
+window.launch_apps.recommended = [];
// Is puter being loaded inside an iframe?
if (window.location !== window.parent.location) {
- window.is_embedded = true;
- // taskbar is not needed in embedded mode
- window.taskbar_height = 0;
+ window.is_embedded = true;
+ // taskbar is not needed in embedded mode
+ window.taskbar_height = 0;
} else {
- window.is_embedded = false;
+ window.is_embedded = false;
}
// calculate desktop height and width
-window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
+window.desktop_height =
+ window.innerHeight - window.toolbar_height - window.taskbar_height;
window.desktop_width = window.innerWidth;
// recalculate desktop height and width on window resize
-$( window ).on( "resize", function() {
- window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
- window.desktop_width = window.innerWidth;
+$(window).on("resize", function () {
+ const radio = window.desktop_width / window.innerWidth;
+
+ window.desktop_height =
+ window.innerHeight - window.toolbar_height - window.taskbar_height;
+ window.desktop_width = window.innerWidth;
+
+ const { top } = $(".window-login").position();
+
+ const width = $(".window-login").width();
+
+ $(".window-login").css({
+ left: (window.desktop_width - width) / 2,
+ top: top / radio,
+ });
});
-
+
// for now `active_element` is basically the last element that was clicked,
-// later on though (todo) `active_element` will also be set by keyboard movements
+// later on though (todo) `active_element` will also be set by keyboard movements
// such as arrow keys, tab key, ... and when creating new windows...
window.active_element = null;
@@ -159,7 +175,7 @@ window.launch_recent_apps_count = 10;
// if yes, which one?
window.current_active_snap_zone = undefined;
-//
+//
window.is_fullpage_mode = false;
window.window_border_radius = 4;
@@ -167,10 +183,10 @@ window.window_border_radius = 4;
window.sites = [];
window.feature_flags = {
- // if true, the user will be able to create shortcuts to files and directories
- create_shortcut: true,
- // if true, the user will be asked to confirm before navigating away from Puter only if there is at least one window open
- prompt_user_when_navigation_away_from_puter: false,
- // if true, the user will be able to zip and download directories
- download_directory: true,
-}
+ // if true, the user will be able to create shortcuts to files and directories
+ create_shortcut: true,
+ // if true, the user will be asked to confirm before navigating away from Puter only if there is at least one window open
+ prompt_user_when_navigation_away_from_puter: false,
+ // if true, the user will be able to zip and download directories
+ download_directory: true,
+};
From 404a6a5aa56060d31f02a6ca3e9bc3c73abf06cd Mon Sep 17 00:00:00 2001
From: meetqy
Date: Sat, 16 Mar 2024 13:53:25 +0800
Subject: [PATCH 15/24] format
---
src/globals.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/globals.js b/src/globals.js
index 222e779ce..11f2cb31d 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -147,6 +147,7 @@ window.desktop_width = window.innerWidth;
// recalculate desktop height and width on window resize
$(window).on("resize", function () {
+
const radio = window.desktop_width / window.innerWidth;
window.desktop_height =
From 68cd3f420e70c0162f0197e6dd977367050e0e45 Mon Sep 17 00:00:00 2001
From: meetqy
Date: Sat, 16 Mar 2024 13:56:36 +0800
Subject: [PATCH 16/24] code format
---
src/globals.js | 142 +++++++++++++++++++++++--------------------------
1 file changed, 67 insertions(+), 75 deletions(-)
diff --git a/src/globals.js b/src/globals.js
index 11f2cb31d..5e31a7c3d 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -7,17 +7,17 @@
* 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 .
*/
-window.clipboard_op = "";
+window.clipboard_op = '';
window.clipboard = [];
window.actions_history = [];
window.window_nav_history = {};
@@ -43,65 +43,63 @@ window.mouseX = 0;
window.mouseY = 0;
// get all logged-in users
-try {
- window.logged_in_users = JSON.parse(localStorage.getItem("logged_in_users"));
-} catch (e) {
- window.logged_in_users = [];
+try{
+ window.logged_in_users = JSON.parse(localStorage.getItem("logged_in_users"));
+}catch(e){
+ window.logged_in_users = [];
}
-if (window.logged_in_users === null) window.logged_in_users = [];
+if(window.logged_in_users === null)
+ window.logged_in_users = [];
// this sessions's user
window.auth_token = localStorage.getItem("auth_token");
-try {
- window.user = JSON.parse(localStorage.getItem("user"));
-} catch (e) {
- window.user = null;
+try{
+ window.user = JSON.parse(localStorage.getItem("user"));
+}catch(e){
+ window.user = null;
}
// in case this is the first time user is visiting multi-user feature
-if (window.logged_in_users.length === 0 && window.user !== null) {
- let tuser = window.user;
- tuser.auth_token = window.auth_token;
- window.logged_in_users.push(tuser);
- localStorage.setItem("logged_in_users", window.logged_in_users);
+if(window.logged_in_users.length === 0 && window.user !== null){
+ let tuser = window.user;
+ tuser.auth_token = window.auth_token
+ window.logged_in_users.push(tuser);
+ localStorage.setItem("logged_in_users", window.logged_in_users);
}
window.last_window_zindex = 1;
// first visit tracker
-window.first_visit_ever =
- localStorage.getItem("has_visited_before") === null ? true : false;
+window.first_visit_ever = localStorage.getItem("has_visited_before") === null ? true : false;
localStorage.setItem("has_visited_before", true);
// system paths
-if (window.user !== undefined && window.user !== null) {
- window.desktop_path = "/" + window.user.username + "/Desktop";
- window.trash_path = "/" + window.user.username + "/Trash";
- window.appdata_path = "/" + window.user.username + "/AppData";
- window.documents_path = "/" + window.user.username + "/Documents";
- window.pictures_path = "/" + window.user.username + "/Photos";
- window.videos_path = "/" + window.user.username + "/Videos";
- window.audio_path = "/" + window.user.username + "/Audio";
- window.home_path = "/" + window.user.username;
+if(window.user !== undefined && window.user !== null){
+ window.desktop_path = '/' + window.user.username + '/Desktop';
+ window.trash_path = '/' + window.user.username + '/Trash';
+ window.appdata_path = '/' + window.user.username + '/AppData';
+ window.documents_path = '/' + window.user.username + '/Documents';
+ window.pictures_path = '/' + window.user.username + '/Photos';
+ window.videos_path = '/' + window.user.username + '/Videos';
+ window.audio_path = '/' + window.user.username + '/Audio';
+ window.home_path = '/' + window.user.username;
}
-window.root_dirname = "Puter";
+window.root_dirname = 'Puter';
// user preferences, persisted across sessions, cached in localStorage
try {
- window.user_preferences = JSON.parse(
- localStorage.getItem("user_preferences")
- );
-} catch (e) {
- window.user_preferences = null;
+ window.user_preferences = JSON.parse(localStorage.getItem('user_preferences'))
+}catch(e){
+ window.user_preferences = null;
}
// default values
if (window.user_preferences === null) {
- window.user_preferences = {
- show_hidden_files: false,
- };
+ window.user_preferences = {
+ show_hidden_files: false,
+ }
}
-window.window_stack = [];
+window.window_stack = []
window.toolbar_height = 30;
window.default_taskbar_height = 50;
window.taskbar_height = window.default_taskbar_height;
@@ -114,58 +112,52 @@ window.operation_id = 0;
window.operation_cancelled = [];
window.last_enter_pressed_to_rename_ts = 0;
window.window_counter = 0;
-window.keypress_item_seach_term = "";
+window.keypress_item_seach_term = '';
window.keypress_item_seach_buffer_timeout = undefined;
window.first_visit_animation = false;
window.show_twitter_link = true;
window.animate_window_opening = true;
window.animate_window_closing = true;
-window.desktop_loading_fade_delay =
- window.first_visit_ever && first_visit_animation ? 6000 : 1000;
+window.desktop_loading_fade_delay = (window.first_visit_ever && first_visit_animation ? 6000 : 1000);
window.watchItems = [];
window.appdata_signatures = {};
window.appCallbackFunctions = [];
// 'Launch' apps
window.launch_apps = [];
-window.launch_apps.recent = [];
-window.launch_apps.recommended = [];
+window.launch_apps.recent = []
+window.launch_apps.recommended = []
// Is puter being loaded inside an iframe?
if (window.location !== window.parent.location) {
- window.is_embedded = true;
- // taskbar is not needed in embedded mode
- window.taskbar_height = 0;
+ window.is_embedded = true;
+ // taskbar is not needed in embedded mode
+ window.taskbar_height = 0;
} else {
- window.is_embedded = false;
+ window.is_embedded = false;
}
// calculate desktop height and width
-window.desktop_height =
- window.innerHeight - window.toolbar_height - window.taskbar_height;
+window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
window.desktop_width = window.innerWidth;
// recalculate desktop height and width on window resize
-$(window).on("resize", function () {
-
- const radio = window.desktop_width / window.innerWidth;
-
- window.desktop_height =
- window.innerHeight - window.toolbar_height - window.taskbar_height;
- window.desktop_width = window.innerWidth;
-
- const { top } = $(".window-login").position();
-
- const width = $(".window-login").width();
-
- $(".window-login").css({
- left: (window.desktop_width - width) / 2,
- top: top / radio,
- });
+$( window ).on( "resize", function() {
+ const radio = window.desktop_width / window.innerWidth;
+
+ window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
+ window.desktop_width = window.innerWidth;
+
+ const { top } = $(".window-login").position();
+ const width = $(".window-login").width();
+ $(".window-login").css({
+ left: (window.desktop_width - width) / 2,
+ top: top / radio,
+ });
});
-
+
// for now `active_element` is basically the last element that was clicked,
-// later on though (todo) `active_element` will also be set by keyboard movements
+// later on though (todo) `active_element` will also be set by keyboard movements
// such as arrow keys, tab key, ... and when creating new windows...
window.active_element = null;
@@ -176,7 +168,7 @@ window.launch_recent_apps_count = 10;
// if yes, which one?
window.current_active_snap_zone = undefined;
-//
+//
window.is_fullpage_mode = false;
window.window_border_radius = 4;
@@ -184,10 +176,10 @@ window.window_border_radius = 4;
window.sites = [];
window.feature_flags = {
- // if true, the user will be able to create shortcuts to files and directories
- create_shortcut: true,
- // if true, the user will be asked to confirm before navigating away from Puter only if there is at least one window open
- prompt_user_when_navigation_away_from_puter: false,
- // if true, the user will be able to zip and download directories
- download_directory: true,
-};
+ // if true, the user will be able to create shortcuts to files and directories
+ create_shortcut: true,
+ // if true, the user will be asked to confirm before navigating away from Puter only if there is at least one window open
+ prompt_user_when_navigation_away_from_puter: false,
+ // if true, the user will be able to zip and download directories
+ download_directory: true,
+}
From 509a6cbd314fa843287b1ac9ff6d0ab52060739d Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Sat, 16 Mar 2024 12:08:11 -0700
Subject: [PATCH 17/24] refactor to account for signup window and the location
of the code
---
src/globals.js | 15 ---------------
src/initgui.js | 28 ++++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/src/globals.js b/src/globals.js
index 5e31a7c3d..3c443394d 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -141,21 +141,6 @@ if (window.location !== window.parent.location) {
window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
window.desktop_width = window.innerWidth;
-// recalculate desktop height and width on window resize
-$( window ).on( "resize", function() {
- const radio = window.desktop_width / window.innerWidth;
-
- window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
- window.desktop_width = window.innerWidth;
-
- const { top } = $(".window-login").position();
- const width = $(".window-login").width();
- $(".window-login").css({
- left: (window.desktop_width - width) / 2,
- top: top / radio,
- });
-});
-
// for now `active_element` is basically the last element that was clicked,
// later on though (todo) `active_element` will also be set by keyboard movements
// such as arrow keys, tab key, ... and when creating new windows...
diff --git a/src/initgui.js b/src/initgui.js
index 097dd0374..121594f4a 100644
--- a/src/initgui.js
+++ b/src/initgui.js
@@ -1976,4 +1976,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
From 8100edbef9367bbb33a0ba6e5847dec731f43744 Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Sat, 16 Mar 2024 19:57:33 -0700
Subject: [PATCH 18/24] implement query param passing between browser window
and app iframe
---
src/UI/UIDesktop.js | 1 +
src/helpers.js | 9 +++++++++
src/initgui.js | 7 +++++++
3 files changed, 17 insertions(+)
diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js
index 2926f433d..f16dbb2f0 100644
--- a/src/UI/UIDesktop.js
+++ b/src/UI/UIDesktop.js
@@ -930,6 +930,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/helpers.js b/src/helpers.js
index a12fb405a..753f06202 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,14 @@ window.launch_app = async (options)=>{
else if(options.token){
iframe_url.searchParams.append('puter.auth.token', options.token);
}
+
+ // if options.params is set, add them to the URL as query params
+ if(options.params && options.params.length > 0){
+ for (const property in options.params) {
+ iframe_url.searchParams.append(property, options.params[property]);
+ }
+ }
+
// Try to acquire app token from the server
else{
let response = await fetch(window.api_origin + "/auth/get-user-app-token", {
diff --git a/src/initgui.js b/src/initgui.js
index 121594f4a..2b9392445 100644
--- a/src/initgui.js
+++ b/src/initgui.js
@@ -76,6 +76,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;
+ }
}
//--------------------------------------------------------------------------------------
From ec984ac81dae3cfdca338d8b2babed2c8a635cec Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Sat, 16 Mar 2024 19:59:36 -0700
Subject: [PATCH 19/24] Remove redundant logic
---
src/helpers.js | 7 -------
1 file changed, 7 deletions(-)
diff --git a/src/helpers.js b/src/helpers.js
index 753f06202..abd9e422a 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -1941,13 +1941,6 @@ window.launch_app = async (options)=>{
iframe_url.searchParams.append('puter.auth.token', options.token);
}
- // if options.params is set, add them to the URL as query params
- if(options.params && options.params.length > 0){
- for (const property in options.params) {
- iframe_url.searchParams.append(property, options.params[property]);
- }
- }
-
// Try to acquire app token from the server
else{
let response = await fetch(window.api_origin + "/auth/get-user-app-token", {
From f3e4a12e57c5c89d8db6da93bc3eb8bdff723678 Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Sat, 16 Mar 2024 20:13:48 -0700
Subject: [PATCH 20/24] Refactor `helpers.js` to reduce file size
---
src/UI/UIDesktop.js | 3 +-
src/UI/UIWindow.js | 3 +-
src/helpers.js | 87 -------------------
.../determine_active_container_parent.js | 43 +++++++++
src/helpers/new_context_menu_item.js | 78 +++++++++++++++++
src/initgui.js | 1 +
6 files changed, 126 insertions(+), 89 deletions(-)
create mode 100644 src/helpers/determine_active_container_parent.js
create mode 100644 src/helpers/new_context_menu_item.js
diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js
index f16dbb2f0..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),
// -------------------------------------------
// -
// -------------------------------------------
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/helpers.js b/src/helpers.js
index abd9e422a..7b0b795ed 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -1979,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,
@@ -2243,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.
*
@@ -3127,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 2b9392445..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);
From 86080b0ccf60776326008e94abb7a316caa030b5 Mon Sep 17 00:00:00 2001
From: Nariman Jelveh
Date: Sun, 17 Mar 2024 16:01:06 -0700
Subject: [PATCH 21/24] Add i18n for English, Korean, and Chinese
This is WIP. Help make the translations better.
---
src/UI/PuterDialog.js | 8 +-
src/UI/UIDesktop.js | 46 +-
src/UI/UIItem.js | 65 ++-
src/UI/UIPrompt.js | 8 +-
src/UI/UITaskbar.js | 8 +-
src/UI/UITaskbarItem.js | 12 +-
src/UI/UIWindow.js | 52 +--
src/UI/UIWindowChangePassword.js | 12 +-
src/UI/UIWindowChangeUsername.js | 10 +-
src/UI/UIWindowClaimReferral.js | 6 +-
src/UI/UIWindowColorPicker.js | 4 +-
src/UI/UIWindowConfirmDownload.js | 10 +-
src/UI/UIWindowCopyProgress.js | 4 +-
src/UI/UIWindowDesktopBGSettings.js | 28 +-
src/UI/UIWindowDownloadDirProg.js | 2 +-
src/UI/UIWindowDownloadProgress.js | 2 +-
src/UI/UIWindowEmailConfirmationRequired.js | 4 +-
src/UI/UIWindowFeedback.js | 8 +-
src/UI/UIWindowFontPicker.js | 2 +-
src/UI/UIWindowGetCopyLink.js | 6 +-
src/UI/UIWindowItemProperties.js | 6 +-
src/UI/UIWindowLogin.js | 10 +-
src/UI/UIWindowMoveProgress.js | 2 +-
src/UI/UIWindowMyWebsites.js | 13 +-
src/UI/UIWindowNewFolderProgress.js | 2 +-
src/UI/UIWindowNewPassword.js | 6 +-
src/UI/UIWindowProgressEmptyTrash.js | 4 +-
src/UI/UIWindowPublishWebsite.js | 6 +-
src/UI/UIWindowQR.js | 2 +-
src/UI/UIWindowRecoverPassword.js | 6 +-
src/UI/UIWindowRefer.js | 8 +-
src/UI/UIWindowSaveAccount.js | 16 +-
src/UI/UIWindowSessionList.js | 6 +-
src/UI/UIWindowSignup.js | 14 +-
src/UI/UIWindowUploadProgress.js | 6 +-
src/helpers.js | 12 +-
src/helpers/new_context_menu_item.js | 10 +-
src/i18n/i18n.js | 477 ++++++++++++++++++++
src/initgui.js | 4 +
src/static-assets.js | 1 +
40 files changed, 688 insertions(+), 220 deletions(-)
create mode 100644 src/i18n/i18n.js
diff --git a/src/UI/PuterDialog.js b/src/UI/PuterDialog.js
index 648ad0c2d..21ffc8220 100644
--- a/src/UI/PuterDialog.js
+++ b/src/UI/PuterDialog.js
@@ -26,11 +26,11 @@ async function PuterDialog(options) {
This website uses Puter to bring you safe, secure, and private AI and Cloud features.
`;
// msg
- h += `Downloading ${options.item_name ?? ''}`;
+ h += `${i18n('downloading')} ${options.item_name ?? ''}`;
h += `
`;
// Progress
h += `
`;
diff --git a/src/UI/UIWindowEmailConfirmationRequired.js b/src/UI/UIWindowEmailConfirmationRequired.js
index 14b7aaf56..88eb9ca70 100644
--- a/src/UI/UIWindowEmailConfirmationRequired.js
+++ b/src/UI/UIWindowEmailConfirmationRequired.js
@@ -47,10 +47,10 @@ function UIWindowEmailConfirmationRequired(options){
h += ``;
h += ``;
h += `
`;
- h += `Re-send Confirmation Code`;
+ h += `${i18n('resend_confirmation_code')}`;
if(options.logout_in_footer){
h += ` • `;
- h += `Log Out`;
+ h += `${i18n('log_out')}`;
}
h += `
`;
h += `
`;
diff --git a/src/UI/UIWindowFeedback.js b/src/UI/UIWindowFeedback.js
index aaba86ce3..5e0b18f7c 100644
--- a/src/UI/UIWindowFeedback.js
+++ b/src/UI/UIWindowFeedback.js
@@ -28,18 +28,18 @@ async function UIWindowQR(options){
// success
h += `
`;
h += ``;
- h += `
Thank you for contacting us. If you have an email associated with your account, you will hear back from us as soon as possible.
`;
+ h += `
${i18n('feedback_sent_confirmation')}
`;
h+= `
`;
// form
h += `
`;
- h += `
Please use the form below to send us your feedback, comments, and bug reports.
`;
+ h += `
${i18n('feedback_c2a')}
`;
h += ``;
- h += ``;
+ h += ``;
h += `
`;
h += `
`;
const el_window = await UIWindow({
- title: 'Contact Us',
+ title: i18n('contact_us'),
app: 'feedback',
single_instance: true,
icon: null,
diff --git a/src/UI/UIWindowFontPicker.js b/src/UI/UIWindowFontPicker.js
index 5e1b23b81..f4208b140 100644
--- a/src/UI/UIWindowFontPicker.js
+++ b/src/UI/UIWindowFontPicker.js
@@ -60,7 +60,7 @@ async function UIWindowFontPicker(options){
h += ``;
// Select
- h += ``
+ h += ``
h += ``;
h += ``;
h += ``;
diff --git a/src/UI/UIWindowGetCopyLink.js b/src/UI/UIWindowGetCopyLink.js
index 8f81790e0..769383f28 100644
--- a/src/UI/UIWindowGetCopyLink.js
+++ b/src/UI/UIWindowGetCopyLink.js
@@ -69,7 +69,11 @@ async function UIWindowGetCopyLink(options){
$(el_window).find('.window-body .downloadable-link').val(url);
$(el_window).find('.window-body .share-copy-link-on-social').on('click', function(e){
- const social_links = socialLink({url: url, title: `Get a copy of '${options.name}' on Puter.com!`, description: `Get a copy of '${options.name}' on Puter.com!`});
+ const social_links = socialLink({
+ url: url,
+ title: i18n('get_a_copy_of_on_puter', options.name, false),
+ description: i18n('get_a_copy_of_on_puter', options.name, false),
+ });
let social_links_html = ``;
social_links_html += `
`;
diff --git a/src/UI/UIWindowItemProperties.js b/src/UI/UIWindowItemProperties.js
index db9aaafec..821483b12 100644
--- a/src/UI/UIWindowItemProperties.js
+++ b/src/UI/UIWindowItemProperties.js
@@ -25,8 +25,8 @@ async function UIWindowItemProperties(item_name, item_path, item_uid, left, top,
h += `
`;
// tabs
h += `
`;
- h += `
General
`;
- h += `
Versions
`;
+ h += `
${i18n('general')}
`;
+ h += `
${i18n('versions')}
`;
h += `
`;
h+= `
`;
@@ -44,7 +44,7 @@ async function UIWindowItemProperties(item_name, item_path, item_uid, left, top,
h += `
Versions
`;
h += `
Associated Websites
`;
h += `
`;
- h += `
Access Granted To
`;
+ h += `
${i18n('access_granted_to')}
`;
h += ``;
h += `
`;
diff --git a/src/UI/UIWindowLogin.js b/src/UI/UIWindowLogin.js
index c79378846..145a1f846 100644
--- a/src/UI/UIWindowLogin.js
+++ b/src/UI/UIWindowLogin.js
@@ -43,12 +43,12 @@ async function UIWindowLogin(options){
h += ``;
// username/email
h += `
`;
- h += ``;
+ h += ``;
h += ``;
h += `
`;
// password with conditional type based based on options.show_password
h += `
`;
- h += ``;
+ h += ``;
h += ``;
// show/hide icon
h += `
@@ -56,15 +56,15 @@ async function UIWindowLogin(options){
`;
h += `
`;
// login
- h += ``;
+ h += ``;
// password recovery
- h += `
Forgot password?
`;
+ h += `
${i18n('forgot_pass_c2a')}
`;
h += ``;
h += `
`;
// create account link
if(options.show_signup_button === undefined || options.show_signup_button){
h += `
`;
- h += ``;
+ h += ``;
h += `
`;
}
h += `
`;
diff --git a/src/UI/UIWindowMoveProgress.js b/src/UI/UIWindowMoveProgress.js
index 18cb7f9db..2b492b335 100644
--- a/src/UI/UIWindowMoveProgress.js
+++ b/src/UI/UIWindowMoveProgress.js
@@ -29,7 +29,7 @@ async function UIWindowMoveProgress(options){
// Progress report
h +=`
`;
// msg
- h += `Moving `;
+ h += `${i18n('moving')} `;
h += ``;
h += `
`;
// progress
diff --git a/src/UI/UIWindowMyWebsites.js b/src/UI/UIWindowMyWebsites.js
index e1a6c0761..543979203 100644
--- a/src/UI/UIWindowMyWebsites.js
+++ b/src/UI/UIWindowMyWebsites.js
@@ -90,10 +90,10 @@ async function UIWindowMyWebsites(options){
h += `
`;
h += `
`;
h += ``;
- h += `Disassociate Folder`;
+ h += `${i18n('disassociate_dir')}`;
h += `
`;
// text
- h += `Taking a little longer than usual. Please wait...`;
+ h += `${i18n('taking_longer_than_usual')}`;
h += `
`;
h +=``;
h += ``;
diff --git a/src/UI/UIWindowNewPassword.js b/src/UI/UIWindowNewPassword.js
index 15dc3b0a1..204c07713 100644
--- a/src/UI/UIWindowNewPassword.js
+++ b/src/UI/UIWindowNewPassword.js
@@ -34,17 +34,17 @@ async function UIWindowNewPassword(options){
h += ``;
// new password
h += `
`;
- h += ``;
+ h += ``;
h += ``;
h += `
`;
// confirm new password
h += `
`;
- h += ``;
+ h += ``;
h += ``;
h += `
`;
// Change Password
- h += ``;
+ h += ``;
h += ``;
const el_window = await UIWindow({
diff --git a/src/UI/UIWindowProgressEmptyTrash.js b/src/UI/UIWindowProgressEmptyTrash.js
index e771fe78e..b8efabd72 100644
--- a/src/UI/UIWindowProgressEmptyTrash.js
+++ b/src/UI/UIWindowProgressEmptyTrash.js
@@ -29,13 +29,13 @@ async function UIWindowProgressEmptyTrash(options){
// message
h +=`
`;
// text
- h += `Emptying the Trash...`;
+ h += `${i18n('emptying_trash')}`;
h += `
`;
h +=``;
h += ``;
const el_window = await UIWindow({
- title: `Creating New Folder`,
+ title: i18n('emptying_trash'),
icon: window.icons[`app-icon-newfolder.svg`],
uid: null,
is_dir: false,
diff --git a/src/UI/UIWindowPublishWebsite.js b/src/UI/UIWindowPublishWebsite.js
index f5a0ffa4b..a0dbb9cca 100644
--- a/src/UI/UIWindowPublishWebsite.js
+++ b/src/UI/UIWindowPublishWebsite.js
@@ -26,7 +26,7 @@ async function UIWindowPublishWebsite(target_dir_uid, target_dir_name, target_di
// success
h += `
`;
@@ -36,13 +36,13 @@ async function UIWindowPublishWebsite(target_dir_uid, target_dir_name, target_di
h += ``;
// subdomain
h += `
`;
- h += ``;
+ h += ``;
h += `
https://.${window.hosting_domain}
`;
h += `
`;
// uid
h += ``;
// Publish
- h += ``
+ h += ``
h += ``;
h += ``;
diff --git a/src/UI/UIWindowQR.js b/src/UI/UIWindowQR.js
index b66136f70..b69c7a9e0 100644
--- a/src/UI/UIWindowQR.js
+++ b/src/UI/UIWindowQR.js
@@ -27,7 +27,7 @@ async function UIWindowQR(options){
// close button containing the multiplication sign
h += `
×
`;
h += `
`;
- h += `
Scan the code below to log into this session from other devices
`;
+ h += `
${i18n('scan_qr_c2a')}
`;
h += `
`;
const el_window = await UIWindow({
diff --git a/src/UI/UIWindowRecoverPassword.js b/src/UI/UIWindowRecoverPassword.js
index 8e37acef0..fd6591f51 100644
--- a/src/UI/UIWindowRecoverPassword.js
+++ b/src/UI/UIWindowRecoverPassword.js
@@ -26,13 +26,13 @@ function UIWindowRecoverPassword(options){
let h = '';
h += `
`;
- h += `
Recover Password
`;
+ h += `
${i18n('recover_password')}
`;
h += ``;
h += `
`;
diff --git a/src/UI/UIWindowRefer.js b/src/UI/UIWindowRefer.js
index b2fa78bd9..aba0bc0ea 100644
--- a/src/UI/UIWindowRefer.js
+++ b/src/UI/UIWindowRefer.js
@@ -29,8 +29,8 @@ async function UIWindowRefer(options){
h += `
`;
h += `
×
`;
h += ``;
- h += `
Get 1 GB for every friend who creates and confirms an account on Puter. Your friend will get 1 GB too!
`;
- h += ``;
+ h += `
${i18n('refer_friends_c2a')}
`;
+ h += ``;
h += ``;
h += ``
h += ``;
@@ -72,11 +72,11 @@ async function UIWindowRefer(options){
$(el_window).find('.window-body .downloadable-link').val(url);
$(el_window).find('.window-body .share-copy-link-on-social').on('click', function(e){
- const social_links = socialLink({url: url, title: `Get 1 GB of free storage on Puter.com!`, description: `Get 1 GB of free storage on Puter.com!`});
+ const social_links = socialLink({url: url, title: i18n('refer_friends_social_media_c2a'), description: i18n('refer_friends_social_media_c2a')});
let social_links_html = ``;
social_links_html += `