diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml new file mode 100644 index 00000000..ba1aa436 --- /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 30f149d1..2add5966 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 00000000..e74e444d --- /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 18335d90..5520ee00 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 648ad0c2..21ffc822 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 a6168eda..20491ed3 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 adc48ed0..c5732a8a 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 343ae553..6780145f 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 Tab ` , - html_active: `Open in New Tab ` , + html: `${i18n('open_in_new_tab')} ` , + html_active: `${i18n('open_in_new_tab')} ` , onClick: function(){ const website_url = $(e.target).closest('.item').attr('data-website_url'); if(website_url){ @@ -1422,7 +1421,7 @@ $(document).on('contextmenu', '.item-has-website-url-badge', async function(e){ }, // Copy Link { - html: 'Copy Link', + html: i18n('copy_link'), onClick: async function(){ const website_url = $(e.target).closest('.item').attr('data-website_url'); if(website_url){ @@ -1523,7 +1522,7 @@ window.activate_item_name_editor= function(el_item){ } // files in trash cannot be renamed, user should be notified with an Alert. else if(path.dirname($(el_item).attr('data-path')) === window.trash_path){ - UIAlert(`This item can't be renamed because it's in the trash. To rename this item, first drag it out of the Trash.`) + UIAlert(i18n('items_in_trash_cannot_be_renamed')); return; } diff --git a/src/UI/UIPrompt.js b/src/UI/UIPrompt.js index c517b427..333234ae 100644 --- a/src/UI/UIPrompt.js +++ b/src/UI/UIPrompt.js @@ -37,8 +37,8 @@ function UIPrompt(options){ // provide an 'OK' button if no buttons are provided if(!options.buttons || options.buttons.length === 0){ options.buttons = [ - {label: 'Cancel', value: false, type: 'default'}, - {label: 'OK', value: true, type: 'primary'}, + {label: i18n('Cancel'), value: false, type: 'default'}, + {label: i18n('OK'), value: true, type: 'primary'}, ] } @@ -52,8 +52,8 @@ function UIPrompt(options){ // buttons if(options.buttons && options.buttons.length > 0){ h += ` `; } diff --git a/src/UI/UITaskbar.js b/src/UI/UITaskbar.js index 0ab89bb8..9c2a5ac6 100644 --- a/src/UI/UITaskbar.js +++ b/src/UI/UITaskbar.js @@ -50,7 +50,7 @@ async function UITaskbar(options){ //--------------------------------------------- UITaskbarItem({ icon: window.icons['start.svg'], - name: 'Start', + name: i18n('start'), sortable: false, keep_in_taskbar: true, disable_context_menu: true, @@ -95,7 +95,7 @@ async function UITaskbar(options){ // ------------------------------------------- if(launch_apps.recent.length > 0){ // heading - apps_str += `Create 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 += `
`; } - h += ` `; + h += ` `; h += ``; } $(el_window).find('.window-body').html(h); @@ -105,7 +105,7 @@ async function UIWindowMyWebsites(options){ margin-bottom: 50px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - color: #596c7c;">You haven't published any websites!`); + color: #596c7c;">${i18n('no_websites_published')}`); } }, Date.now() - init_ts < 1000 ? 0 : 2000); }) @@ -136,10 +136,11 @@ $(document).on('click', '.mywebsites-site-setting', function(e){ html: `Release Address`, onClick: async function(){ const alert_resp = await UIAlert({ - message: `Are you sure you want to release this address?`, + message: i18n('release_address_confirmation'), buttons:[ { - label: 'Yes, Release It', + label: i18n('yes_release_it'), + value: 'yes', type: 'primary', }, { @@ -147,7 +148,7 @@ $(document).on('click', '.mywebsites-site-setting', function(e){ }, ] }) - if(alert_resp !== 'Yes, Release It'){ + if(alert_resp !== 'yes'){ return; } diff --git a/src/UI/UIWindowNewFolderProgress.js b/src/UI/UIWindowNewFolderProgress.js index 3f01e8af..0f04d7db 100644 --- a/src/UI/UIWindowNewFolderProgress.js +++ b/src/UI/UIWindowNewFolderProgress.js @@ -29,7 +29,7 @@ async function UIWindowNewFolderProgress(options){ // message h +=` `; h +=``; h += ``; diff --git a/src/UI/UIWindowNewPassword.js b/src/UI/UIWindowNewPassword.js index 15dc3b0a..204c0771 100644 --- a/src/UI/UIWindowNewPassword.js +++ b/src/UI/UIWindowNewPassword.js @@ -34,17 +34,17 @@ async function UIWindowNewPassword(options){ h += ``; // new password h += ` `; // confirm new password h += ` `; // Change Password - h += ``; + h += ``; h += ``; const el_window = await UIWindow({ diff --git a/src/UI/UIWindowProgressEmptyTrash.js b/src/UI/UIWindowProgressEmptyTrash.js index e771fe78..b8efabd7 100644 --- a/src/UI/UIWindowProgressEmptyTrash.js +++ b/src/UI/UIWindowProgressEmptyTrash.js @@ -29,13 +29,13 @@ async function UIWindowProgressEmptyTrash(options){ // message 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 f5a0ffa4..a0dbb9cc 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 += `${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 += ``; @@ -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 += `Share to
` + social_links_html += `${i18n('share_to')}
` social_links_html += `` social_links_html += `` social_links_html += `` diff --git a/src/UI/UIWindowSaveAccount.js b/src/UI/UIWindowSaveAccount.js index 2c5a367c..3a57ba9d 100644 --- a/src/UI/UIWindowSaveAccount.js +++ b/src/UI/UIWindowSaveAccount.js @@ -32,39 +32,39 @@ async function UIWindowSaveAccount(options){ // success h += `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 += `