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/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
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
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){
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.
-Powered by Puter.js
-By clicking 'Continue' you agree to Puter's Terms of Service and Privacy Policy.
+${i18n('powered_by_puter_js')}
+${i18n('tos_fineprint')}
`; const el_window = await UIWindow({ diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js index a6168eda4..20491ed3f 100644 --- a/src/UI/UIContextMenu.js +++ b/src/UI/UIContextMenu.js @@ -145,48 +145,70 @@ function UIContextMenu(options){ $(contextMenu).remove(); }); } - 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'); - }) + // initialize menuAim plugin (../libs/jquery.menu-aim.js) + $(contextMenu).menuAim({ + submenuDirection: function(){ + //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 { + return "left"; + } + } + }, + //activates item when mouse enters depending in mouse position and direction + activate: function (e) { - // 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(); + //activate items + let item = $(e).closest('.context-menu-item'); - // 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, - } - }) + // mark other items as inactive + $(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active'); + // mark this item as 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 + $(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, + } + }) + } + } + }, + //deactivates row when mouse leavess + deactivate: function (e) { + //deactivate submenu + if($(e).hasClass('context-menu-item-submenu')){ + $(`.context-menu[data-id="${menu_id}-${$(e).attr('data-action')}"]`).remove(); + } } - 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(); @@ -227,4 +249,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/UI/UIDesktop.js b/src/UI/UIDesktop.js index adc48ed03..c5732a8a4 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 = ''; @@ -621,7 +622,7 @@ async function UIDesktop(options){ // Sort by // ------------------------------------------- { - html: "Sort by", + html: i18n('sort_by'), items: [ { html: `Auto Arrange`, @@ -645,6 +646,7 @@ async function UIDesktop(options){ { html: `Name`, disabled: !is_auto_arrange_enabled, + html: i18n('name'), icon: $(el_desktop).attr('data-sort_by') === 'name' ? '✓' : '', onClick: async function(){ sort_items(el_desktop, 'name', $(el_desktop).attr('data-sort_order')); @@ -654,6 +656,7 @@ async function UIDesktop(options){ { html: `Date modified`, disabled: !is_auto_arrange_enabled, + html: i18n('date_modified'), icon: $(el_desktop).attr('data-sort_by') === 'modified' ? '✓' : '', onClick: async function(){ sort_items(el_desktop, 'modified', $(el_desktop).attr('data-sort_order')); @@ -663,6 +666,7 @@ async function UIDesktop(options){ { html: `Type`, disabled: !is_auto_arrange_enabled, + html: i18n('type'), icon: $(el_desktop).attr('data-sort_by') === 'type' ? '✓' : '', onClick: async function(){ sort_items(el_desktop, 'type', $(el_desktop).attr('data-sort_order')); @@ -672,6 +676,7 @@ async function UIDesktop(options){ { html: `Size`, disabled: !is_auto_arrange_enabled, + html: i18n('size'), icon: $(el_desktop).attr('data-sort_by') === 'size' ? '✓' : '', onClick: async function(){ sort_items(el_desktop, 'size', $(el_desktop).attr('data-sort_order')); @@ -685,6 +690,7 @@ async function UIDesktop(options){ { html: `Ascending`, disabled: !is_auto_arrange_enabled, + html: i18n('ascending'), icon: $(el_desktop).attr('data-sort_order') === 'asc' ? '✓' : '', onClick: async function(){ const sort_by = $(el_desktop).attr('data-sort_by') @@ -695,6 +701,7 @@ async function UIDesktop(options){ { html: `Descending`, disabled: !is_auto_arrange_enabled, + html: i18n('descending'), icon: $(el_desktop).attr('data-sort_order') === 'desc' ? '✓' : '', onClick: async function(){ const sort_by = $(el_desktop).attr('data-sort_by') @@ -708,7 +715,7 @@ async function UIDesktop(options){ // Refresh // ------------------------------------------- { - html: "Refresh", + html: i18n('refresh'), onClick: function(){ refresh_item_container(el_desktop); } @@ -717,7 +724,8 @@ async function UIDesktop(options){ // Show/Hide hidden files // ------------------------------------------- { - html: `${window.user_preferences.show_hidden_files ? 'Hide' : 'Show'} hidden files`, + html: i18n('show_hidden'), + icon: window.user_preferences.show_hidden_files ? '✓' : '', onClick: function(){ window.mutate_user_preferences({ show_hidden_files : !window.user_preferences.show_hidden_files, @@ -732,7 +740,7 @@ async function UIDesktop(options){ // ------------------------------------------- // New File // ------------------------------------------- - window.new_context_menu_item(desktop_path, el_desktop), + new_context_menu_item(desktop_path, el_desktop), // ------------------------------------------- // - // ------------------------------------------- @@ -741,7 +749,7 @@ async function UIDesktop(options){ // Paste // ------------------------------------------- { - html: "Paste", + html: i18n('paste'), disabled: clipboard.length > 0 ? false : true, onClick: function(){ if(clipboard_op === 'copy') @@ -754,7 +762,7 @@ async function UIDesktop(options){ // Undo // ------------------------------------------- { - html: "Undo", + html: i18n('undo'), disabled: actions_history.length > 0 ? false : true, onClick: function(){ undo_last_action(); @@ -764,21 +772,12 @@ async function UIDesktop(options){ // Upload Here // ------------------------------------------- { - html: "Upload Here", + html: i18n('upload_here'), onClick: function(){ init_upload_using_dialog(el_desktop); } }, // ------------------------------------------- - // Request Files - // ------------------------------------------- - // { - // html: "Request Files", - // onClick: function(){ - // UIWindowRequestFiles({dir_path: desktop_path}) - // } - // }, - // ------------------------------------------- // - // ------------------------------------------- '-', @@ -786,7 +785,7 @@ async function UIDesktop(options){ // Change Desktop Background… // ------------------------------------------- { - html: "Change Desktop Background…", + html: i18n('change_desktop_background'), onClick: function(){ UIWindowDesktopBGSettings(); } @@ -955,6 +954,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, @@ -1159,7 +1159,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // My Websites //-------------------------------------------------- { - html: "My Websites", + html: i18n('my_websites'), onClick: async function(){ UIWindowMyWebsites(); } @@ -1168,7 +1168,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // Change Username //-------------------------------------------------- { - html: "Change Username", + html: i18n('change_username'), onClick: async function(){ UIWindowChangeUsername(); } @@ -1178,7 +1178,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // Change Password //-------------------------------------------------- { - html: "Change Password", + html: i18n('change_password'), onClick: async function(){ UIWindowChangePassword(); } @@ -1187,7 +1187,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // Contact Us //-------------------------------------------------- { - html: "Contact Us", + html: i18n('contact_us'), onClick: async function(){ UIWindowFeedback(); } @@ -1201,7 +1201,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // Log Out //-------------------------------------------------- { - html: "Log Out", + html: i18n('log_out'), onClick: async function(){ // see if there are any open windows, if yes notify user if($('.window-app').length > 0){ diff --git a/src/UI/UIItem.js b/src/UI/UIItem.js index 343ae5531..6780145f2 100644 --- a/src/UI/UIItem.js +++ b/src/UI/UIItem.js @@ -760,7 +760,7 @@ function UIItem(options){ // ------------------------------------------- if(are_trashed){ menu_items.push({ - html: "Restore", + html: i18n('restore'), onClick: function(){ $selected_items.each(function() { const ell = this; @@ -779,7 +779,7 @@ function UIItem(options){ // Donwload // ------------------------------------------- menu_items.push({ - html: 'Download', + html: i18n('Download'), onClick: async function(){ let items = []; for (let index = 0; index < $selected_items.length; index++) { @@ -793,7 +793,7 @@ function UIItem(options){ // Zip // ------------------------------------------- menu_items.push({ - html: 'Zip', + html: i18n('zip'), onClick: async function(){ let items = []; for (let index = 0; index < $selected_items.length; index++) { @@ -812,7 +812,7 @@ function UIItem(options){ // Cut // ------------------------------------------- menu_items.push({ - html: "Cut", + html: i18n('cut'), onClick: function(){ window.clipboard_op= 'move'; window.clipboard = []; @@ -828,7 +828,7 @@ function UIItem(options){ // ------------------------------------------- if(!are_trashed){ menu_items.push({ - html: "Copy", + html: i18n('copy'), onClick: function(){ window.clipboard_op= 'copy'; window.clipboard = []; @@ -848,7 +848,7 @@ function UIItem(options){ // ------------------------------------------- if(are_trashed){ menu_items.push({ - html: 'Delete Permanently', + html: i18n('delete_permanently'), onClick: async function(){ const alert_resp = await UIAlert({ message: `Are you sure you want to permanently delete these items?`, @@ -887,7 +887,7 @@ function UIItem(options){ // ------------------------------------------- if(!are_trashed && window.feature_flags.create_shortcut){ menu_items.push({ - html: 'Create Shortcut', + html: i18n('create_shortcut'), onClick: async function(){ $selected_items.each(function() { let base_dir = path.dirname($(this).attr('data-path')); @@ -913,7 +913,7 @@ function UIItem(options){ // ------------------------------------------- if(!are_trashed){ menu_items.push({ - html: 'Delete', + html: i18n('delete'), onClick: async function(){ move_items($selected_items, trash_path); } @@ -933,7 +933,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed){ menu_items.push({ - html: 'Open', + html: i18n('open'), onClick: function(){ open_item({item: el_item}); } @@ -989,7 +989,7 @@ function UIItem(options){ } // add all suitable apps menu_items.push({ - html: 'Open With', + html: i18n('open_with'), items: items, }); @@ -1005,7 +1005,7 @@ function UIItem(options){ // ------------------------------------------- if($(el_item).closest('.window-body').length > 0 && options.is_dir){ menu_items.push({ - html: 'Open in New Window', + html: i18n('open_in_new_window'), onClick: function(){ if(options.is_dir){ open_item({item: el_item, new_window: true}) @@ -1024,7 +1024,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed && !is_trash && options.is_dir){ menu_items.push({ - html: 'Publish As Website', + html: i18n('publish_as_website'), disabled: !options.is_dir, onClick: async function () { if(window.require_email_verification_to_publish_website){ @@ -1051,7 +1051,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed && !is_trash && options.is_dir){ menu_items.push({ - html: 'Deploy As App', + html: i18n('deploy_as_app'), disabled: !options.is_dir, onClick: async function () { launch_app({ @@ -1073,19 +1073,18 @@ function UIItem(options){ // ------------------------------------------- if(is_trash){ menu_items.push({ - html: 'Empty Trash', + html: i18n('empty_trash'), onClick: async function(){ empty_trash(); } }); - } // ------------------------------------------- // Donwload // ------------------------------------------- if(!is_trash && !is_trashed && (options.associated_app_name === null || options.associated_app_name === undefined)){ menu_items.push({ - html: 'Download', + html: i18n('Download'), disabled: options.is_dir && !window.feature_flags.download_directory, onClick: async function(){ if(options.is_dir) @@ -1102,11 +1101,11 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed && !is_trash && (options.associated_app_name === null || options.associated_app_name === undefined)){ menu_items.push({ - html: 'Get Copy Link', + html: i18n('get_copy_link'), onClick: async function(){ if(window.user.is_temp && !await UIWindowSaveAccount({ - message: 'Please create an account to proceed.', + message: i18n('save_account_to_get_copy_link'), send_confirmation_code: true, window_options: { backdrop: true, @@ -1131,7 +1130,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trash && !is_trashed && !$(el_item).attr('data-path').endsWith('.zip')){ menu_items.push({ - html: "Zip", + html: i18n('zip'), onClick: function(){ zipItems(el_item, path.dirname($(el_item).attr('data-path')), false); } @@ -1142,7 +1141,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trash && !is_trashed && $(el_item).attr('data-path').endsWith('.zip')){ menu_items.push({ - html: "Unzip", + html: i18n('unzip'), onClick: async function(){ const zip = new JSZip(); let filPath = $(el_item).attr('data-path'); @@ -1170,7 +1169,7 @@ function UIItem(options){ // ------------------------------------------- if(is_trashed){ menu_items.push({ - html: 'Restore', + html: i18n('restore'), onClick: async function(){ let metadata = $(el_item).attr('data-metadata') === '' ? {} : JSON.parse($(el_item).attr('data-metadata')) move_items([el_item], path.dirname(metadata.original_path)); @@ -1187,7 +1186,7 @@ function UIItem(options){ // ------------------------------------------- if($(el_item).attr('data-immutable') === '0'){ menu_items.push({ - html: "Cut", + html: i18n('cut'), onClick: function(){ window.clipboard_op= 'move'; window.clipboard= [options.path]; @@ -1199,7 +1198,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed && !is_trash){ menu_items.push({ - html: "Copy", + html: i18n('copy'), onClick: function(){ window.clipboard_op= 'copy'; window.clipboard= [{path: options.path}]; @@ -1211,7 +1210,7 @@ function UIItem(options){ // ------------------------------------------- if($(el_item).attr('data-is_dir') === '1' && !is_trashed && !is_trash){ menu_items.push({ - html: "Paste Into Folder", + html: i18n('paste_into_folder'), disabled: clipboard.length > 0 ? false : true, onClick: function(){ if(clipboard_op === 'copy') @@ -1232,7 +1231,7 @@ function UIItem(options){ // ------------------------------------------- if(!is_trashed && window.feature_flags.create_shortcut){ menu_items.push({ - html: 'Create Shortcut', + html: i18n('create_shortcut'), onClick: async function(){ let base_dir = path.dirname($(el_item).attr('data-path')); // Trash on Desktop is a special case @@ -1256,7 +1255,7 @@ function UIItem(options){ // ------------------------------------------- if($(el_item).attr('data-immutable') === '0' && !is_trashed){ menu_items.push({ - html: 'Delete', + html: i18n('delete'), onClick: async function(){ move_items([el_item], trash_path); } @@ -1267,7 +1266,7 @@ function UIItem(options){ // ------------------------------------------- if(is_trashed){ menu_items.push({ - html: 'Delete Permanently', + html: i18n('delete_permanently'), onClick: async function(){ const alert_resp = await UIAlert({ message: `Are you sure you want to permanently delete this item?`, @@ -1304,7 +1303,7 @@ function UIItem(options){ // ------------------------------------------- if($(el_item).attr('data-immutable') === '0' && !is_trashed && !is_trash){ menu_items.push({ - html: "Rename", + html: i18n('rename'), onClick: function(){ activate_item_name_editor(el_item) } @@ -1318,7 +1317,7 @@ function UIItem(options){ // Properties // ------------------------------------------- menu_items.push({ - html: "Properties", + html: i18n('properties'), onClick: function(){ let window_height = 500; let window_width = 450; @@ -1411,8 +1410,8 @@ $(document).on('contextmenu', '.item-has-website-url-badge', async function(e){ items: [ // Open { - html: `Open in New TabCreate an account and confirm your email address to receive 1 GB of free storage. Your friend will get 1 GB of free storage too.
`; - h += ``; + h += `${i18n('confirm_account_for_free_referral_storage_c2a')}
`; + 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+= `Please use the form below to send us your feedback, comments, and bug reports.
`; + h += `${i18n('feedback_c2a')}
`; h += ``; - h += ``; + h += ``; h += `Forgot password?
`; + h += `${i18n('forgot_pass_c2a')}
`; h += ``; h += ``;
h += ``;
- h += `Disassociate Folder`;
+ h += `
${i18n('disassociate_dir')}`;
h += `
${target_dir_name} has been published to:
`; + h += `
${i18n('dir_published_as_website', `${target_dir_name}`)}
`; 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 += `Share to
` + social_links_html += `${i18n('share_to')}
` social_links_html += `` social_links_html += `Thank you for creating an account. This session has been saved.
`; - h += `` + h += `${i18n('session_saved')}
`; + h += `` h+= `${options.message ?? 'Create an account to save your current session and avoid losing your work.'}
`; + h += `${options.message ?? i18n('save_session_c2a')}
`; // signup form h += ``; h += `