Remove settings UI & service; redirect to dashboard (#2900)

This commit is contained in:
Nariman Jelveh
2026-05-04 14:11:56 -07:00
committed by GitHub
parent c6bacca7f6
commit 36de2b9aa4
12 changed files with 4 additions and 1142 deletions
-117
View File
@@ -1,117 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
// About
export default {
id: 'about',
title_i18n_key: 'about',
icon: 'logo-outline.svg',
html: () => {
return `
<div class="about-container">
<div class="about">
<a href="https://puter.com" target="_blank" class="logo"><img src="/images/logo.png"></a>
<p class="description">${i18n('puter_description')}</p>
<p class="links">
<a href="mailto:hey@puter.com" target="_blank">hey@puter.com</a>
<span style="color: #CCC;">•</span>
<a href="https://docs.puter.com" target="_blank">${i18n('developers')}</a>
<span style="color: #CCC;">•</span>
<a href="https://status.puter.com" target="_blank">${i18n('status')}</a>
<span style="color: #CCC;">•</span>
<a href="https://puter.com/terms" target="_blank">${i18n('terms')}</a>
<span style="color: #CCC;">•</span>
<a href="https://puter.com/privacy" target="_blank">${i18n('privacy')}</a>
<span style="color: #CCC;">•</span>
<a href="#" class="show-credits">${i18n('credits')}</a>
</p>
<div class="social-links">
<a href="https://twitter.com/HeyPuter/" target="_blank">
<svg viewBox="0 0 24 24" aria-hidden="true" style="opacity: 0.7;"><g><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path></g></svg>
</a>
<a href="https://github.com/HeyPuter/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48">
<g transform="translate(0, 0)">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#5a606b" d="M24,0.6c-13.3,0-24,10.7-24,24c0,10.6,6.9,19.6,16.4,22.8 c1.2,0.2,1.6-0.5,1.6-1.2c0-0.6,0-2.1,0-4.1c-6.7,1.5-8.1-3.2-8.1-3.2c-1.1-2.8-2.7-3.5-2.7-3.5c-2.2-1.5,0.2-1.5,0.2-1.5 c2.4,0.2,3.7,2.5,3.7,2.5c2.1,3.7,5.6,2.6,7,2c0.2-1.6,0.8-2.6,1.5-3.2c-5.3-0.6-10.9-2.7-10.9-11.9c0-2.6,0.9-4.8,2.5-6.4 c-0.2-0.6-1.1-3,0.2-6.4c0,0,2-0.6,6.6,2.5c1.9-0.5,4-0.8,6-0.8c2,0,4.1,0.3,6,0.8c4.6-3.1,6.6-2.5,6.6-2.5c1.3,3.3,0.5,5.7,0.2,6.4 c1.5,1.7,2.5,3.8,2.5,6.4c0,9.2-5.6,11.2-11,11.8c0.9,0.7,1.6,2.2,1.6,4.4c0,3.2,0,5.8,0,6.6c0,0.6,0.4,1.4,1.7,1.2 C41.1,44.2,48,35.2,48,24.6C48,11.3,37.3,0.6,24,0.6z">
</path>
</g>
</svg>
</a>
<a href="https://discord.gg/PQcx7Teh8u" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48"><g transform="translate(0, 0)"><path d="M19.837,20.3a2.562,2.562,0,0,0,0,5.106,2.562,2.562,0,0,0,0-5.106Zm8.4,0a2.562,2.562,0,1,0,2.346,2.553A2.45,2.45,0,0,0,28.232,20.3Z" fill="#444444" data-color="color-2"></path> <path d="M39.41,1H8.59A4.854,4.854,0,0,0,4,6V37a4.482,4.482,0,0,0,4.59,4.572H34.672l-1.219-4.255L36.4,40.054,39.18,42.63,44,47V6A4.854,4.854,0,0,0,39.41,1ZM30.532,31.038s-.828-.989-1.518-1.863a7.258,7.258,0,0,0,4.163-2.737A13.162,13.162,0,0,1,30.532,27.8a15.138,15.138,0,0,1-3.335.989,16.112,16.112,0,0,1-5.957-.023,19.307,19.307,0,0,1-3.381-.989,13.112,13.112,0,0,1-2.622-1.357,7.153,7.153,0,0,0,4.025,2.714c-.69.874-1.541,1.909-1.541,1.909-5.083-.161-7.015-3.5-7.015-3.5a30.8,30.8,0,0,1,3.312-13.409,11.374,11.374,0,0,1,6.463-2.415l.23.276a15.517,15.517,0,0,0-6.049,3.013s.506-.276,1.357-.667a17.272,17.272,0,0,1,5.221-1.449,2.266,2.266,0,0,1,.391-.046,19.461,19.461,0,0,1,4.646-.046A18.749,18.749,0,0,1,33.2,15.007a15.307,15.307,0,0,0-5.727-2.921l.322-.368a11.374,11.374,0,0,1,6.463,2.415A30.8,30.8,0,0,1,37.57,27.542S35.615,30.877,30.532,31.038Z" fill="#444444"></path></g></svg> </a>
<a href="https://www.linkedin.com/company/puter/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48">
<g transform="translate(0, 0)">
<path fill="#5a606b" d="M46,0H2C0.9,0,0,0.9,0,2v44c0,1.1,0.9,2,2,2h44c1.1,0,2-0.9,2-2V2C48,0.9,47.1,0,46,0z M14.2,40.9H7.1V18 h7.1V40.9z M10.7,14.9c-2.3,0-4.1-1.8-4.1-4.1c0-2.3,1.8-4.1,4.1-4.1c2.3,0,4.1,1.8,4.1,4.1C14.8,13,13,14.9,10.7,14.9z M40.9,40.9 h-7.1V29.8c0-2.7,0-6.1-3.7-6.1c-3.7,0-4.3,2.9-4.3,5.9v11.3h-7.1V18h6.8v3.1h0.1c0.9-1.8,3.3-3.7,6.7-3.7c7.2,0,8.5,4.7,8.5,10.9 V40.9z">
</path>
</g>
</svg>
</a>
</div>
</div>
<div class="version"></div>
<dialog class="credits">
<div class="credit-content">
<p style="margin: 0; font-size: 18px; text-align: center;">${i18n('oss_code_and_content')}</p>
<div style="max-height: 300px; overflow-y: scroll;">
<ul style="padding-left: 25px; padding-top:15px;">
<li>FileSaver.js <a target="_blank" href="https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md">${i18n('license')}</a></li>
<li>html-entities <a target="_blank" href="https://github.com/mdevils/html-entities/blob/master/LICENSE">${i18n('license')}</a></li>
<li>iro.js <a target="_blank" href="https://github.com/jaames/iro.js/blob/master/LICENSE.txt">${i18n('license')}</a></li>
<li>jQuery <a target="_blank" href="https://jquery.org/license/">${i18n('license')}</a></li>
<li>jQuery-dragster <a target="_blank" href="https://github.com/catmanjan/jquery-dragster/blob/master/LICENSE">${i18n('license')}</a></li>
<li>jQuery-menu-aim <a target="_blank" href="https://github.com/kamens/jQuery-menu-aim?tab=readme-ov-file#faq">${i18n('license')}</a></li>
<li>jQuery UI <a target="_blank" href="https://jquery.org/license/">${i18n('license')}</a></li>
<li>mime <a target="_blank" href="https://github.com/broofa/mime/blob/main/LICENSE">${i18n('license')}</a></li>
<li>qrcodejs <a target="_blank" href="https://github.com/davidshimjs/qrcodejs/blob/master/LICENSE">${i18n('license')}</a></li>
<li>Selection <a target="_blank" href="https://github.com/simonwep/selection/blob/master/LICENSE">${i18n('license')}</a></li>
<li>socket.io <a target="_blank" href="https://github.com/socketio/socket.io/blob/main/LICENSE">${i18n('license')}</a></li>
<li>Wallpaper by <a target="_blank" href="https://unsplash.com/@fakurian?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Milad Fakurian</a> on <a target="_blank" href="https://unsplash.com/photos/blue-orange-and-yellow-wallpaper-E8Ufcyxz514?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a></li>
<li>Inter font by The Inter Project Authors <a target="_blank" href="https://github.com/rsms/inter">${i18n('license')}</a></li>
</ul>
</div>
</div>
</dialog>
</div>`;
},
init: ($el_window) => {
// server and version infomration
puter.os.version()
.then(res => {
const deployed_date = new Date(res.deploy_timestamp).toLocaleString();
$el_window.find('.version').html(`Version: ${html_encode(res.version)} &bull; Server: ${html_encode(res.location)} &bull; Deployed: ${html_encode(deployed_date)}`);
})
.catch(error => {
console.error('Failed to fetch server info:', error);
$el_window.find('.version').html('Failed to load version information.');
});
$el_window.find('.credits').on('click', function (e) {
if ( $(e.target).hasClass('credits') ) {
$('.credits').get(0).close();
}
});
$el_window.find('.show-credits').on('click', function (e) {
$('.credits').get(0).showModal();
});
},
};
-171
View File
@@ -1,171 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import UIWindowChangePassword from '../UIWindowChangePassword.js';
import UIWindowChangeEmail from './UIWindowChangeEmail.js';
import UIWindowChangeUsername from '../UIWindowChangeUsername.js';
import UIWindowConfirmUserDeletion from './UIWindowConfirmUserDeletion.js';
import UIWindowManageSessions from '../UIWindowManageSessions.js';
import UIWindow from '../UIWindow.js';
// About
export default {
id: 'account',
title_i18n_key: 'account',
icon: 'user.svg',
html: () => {
let h = '';
// profile picture
h += '<div style="overflow: visible; display: flex; margin-bottom: 20px; flex-direction: column; align-items: center;">';
h += `<div class="profile-picture change-profile-picture" style="background-image: url('${html_encode(window.user?.profile?.picture ?? window.icons['profile.svg'])}');">`;
h += '</div>';
h += '</div>';
// change password button
if ( ! window.user.is_temp ) {
h += '<div class="settings-card">';
h += `<strong>${i18n('password')}</strong>`;
h += '<div style="flex-grow:1;">';
h += `<button class="button change-password" style="float:right;">${i18n('change_password')}</button>`;
h += '</div>';
h += '</div>';
}
// change username button
h += '<div class="settings-card">';
h += '<div>';
h += `<strong style="display:block;">${i18n('username')}</strong>`;
h += `<span class="username" style="display:block; margin-top:5px;">${html_encode(window.user.username)}</span>`;
h += '</div>';
h += '<div style="flex-grow:1;">';
h += `<button class="button change-username" style="float:right;">${i18n('change_username')}</button>`;
h += '</div>';
h += '</div>';
// change email button
if ( window.user.email ) {
h += '<div class="settings-card">';
h += '<div>';
h += `<strong style="display:block;">${i18n('email')}</strong>`;
h += `<span class="user-email" style="display:block; margin-top:5px;">${html_encode(window.user.email)}</span>`;
h += '</div>';
h += '<div style="flex-grow:1;">';
h += `<button class="button change-email" style="float:right;">${i18n('change_email')}</button>`;
h += '</div>';
h += '</div>';
}
// 'Delete Account' button
h += '<div class="settings-card settings-card-danger">';
h += `<strong style="display: inline-block;">${i18n('delete_account')}</strong>`;
h += '<div style="flex-grow:1;">';
h += `<button class="button button-danger delete-account" style="float:right;">${i18n('delete_account')}</button>`;
h += '</div>';
h += '</div>';
return h;
},
init: ($el_window) => {
$el_window.find('.change-password').on('click', function (e) {
UIWindowChangePassword({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.change-username').on('click', function (e) {
UIWindowChangeUsername({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.change-email').on('click', function (e) {
UIWindowChangeEmail({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.manage-sessions').on('click', function (e) {
UIWindowManageSessions({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.delete-account').on('click', function (e) {
UIWindowConfirmUserDeletion({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.change-profile-picture').on('click', async function (e) {
// open dialog
UIWindow({
path: `/${ window.user.username }/Desktop`,
// this is the uuid of the window to which this dialog will return
parent_uuid: $el_window.attr('data-element_uuid'),
allowed_file_types: ['.png', '.jpg', '.jpeg'],
show_maximize_button: false,
show_minimize_button: false,
title: 'Open',
is_dir: true,
is_openFileDialog: true,
selectable_body: false,
});
});
$el_window.on('file_opened', async function (e) {
let selected_file = Array.isArray(e.detail) ? e.detail[0] : e.detail;
// set profile picture
const profile_pic = await puter.fs.read(selected_file.path);
// blob to base64
const reader = new FileReader();
reader.readAsDataURL(profile_pic);
reader.onloadend = function () {
// resizes the image to 150x150
const img = new Image();
img.src = reader.result;
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 150;
canvas.height = 150;
ctx.drawImage(img, 0, 0, 150, 150);
const base64data = canvas.toDataURL('image/png');
// update profile picture everywhere (matches helpers.js session refresh)
$el_window.find('.profile-picture').css('background-image', `url(${ html_encode(base64data) })`);
$('.profile-pic').css('background-image', `url(${ html_encode(base64data) })`);
$('.profile-image').css('background-image', `url(${ html_encode(base64data) })`);
$('.profile-image').addClass('profile-image-has-picture');
// update profile picture
update_profile(window.user.username, { picture: base64data });
};
};
});
},
};
@@ -1,132 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
const shortcutSections = () => ([
{
title: i18n('keyboard_shortcuts_general'),
rows: [
{
action: i18n('keyboard_shortcuts_open_help'),
keys: 'F1 / Ctrl+?',
},
{
action: i18n('keyboard_shortcuts_search'),
keys: 'Ctrl/Cmd + F',
},
{
action: i18n('keyboard_shortcuts_close_window'),
keys: 'Ctrl + W',
},
{
action: i18n('keyboard_shortcuts_undo'),
keys: 'Ctrl/Cmd + Z',
},
{
action: i18n('keyboard_shortcuts_select_all'),
keys: 'Ctrl/Cmd + A',
},
{
action: i18n('keyboard_shortcuts_open_item'),
keys: 'Enter',
},
{
action: i18n('keyboard_shortcuts_close_menus'),
keys: 'Esc',
},
],
},
{
title: i18n('keyboard_shortcuts_navigation'),
rows: [
{
action: i18n('keyboard_shortcuts_arrow_navigation'),
keys: 'Arrow Keys',
},
{
action: i18n('keyboard_shortcuts_type_to_select'),
keys: i18n('keyboard_shortcuts_type_to_select_keys'),
},
],
},
{
title: i18n('keyboard_shortcuts_files'),
rows: [
{
action: i18n('keyboard_shortcuts_copy'),
keys: 'Ctrl/Cmd + C',
},
{
action: i18n('keyboard_shortcuts_cut'),
keys: 'Ctrl/Cmd + X',
},
{
action: i18n('keyboard_shortcuts_paste'),
keys: 'Ctrl/Cmd + V',
},
{
action: i18n('keyboard_shortcuts_delete'),
keys: 'Delete (Win/Linux) / Cmd + Backspace (Mac)',
},
{
action: i18n('keyboard_shortcuts_permanent_delete'),
keys: 'Shift + Delete (Win/Linux) / Option + Cmd + Backspace (Mac)',
},
],
},
]);
export default {
id: 'keyboard-shortcuts',
title_i18n_key: 'keyboard_shortcuts',
icon: 'shortcut.svg',
html: () => {
const sections = shortcutSections();
const sectionHtml = sections.map(section => {
const rows = section.rows.map(row => `
<tr>
<td class="settings-shortcuts-action">${row.action}</td>
<td class="settings-shortcuts-keys"><span>${row.keys}</span></td>
</tr>
`).join('');
return `
<div class="settings-shortcuts-section">
<h2>${section.title}</h2>
<table class="settings-shortcuts-table">
<thead>
<tr>
<th>${i18n('keyboard_shortcuts_action')}</th>
<th>${i18n('keyboard_shortcuts_shortcut')}</th>
</tr>
</thead>
<tbody>
${rows}
</tbody>
</table>
</div>
`;
}).join('');
return `
<h1>${i18n('keyboard_shortcuts')}</h1>
<p class="settings-shortcuts-intro">${i18n('keyboard_shortcuts_intro')}</p>
${sectionHtml}
`;
},
};
-83
View File
@@ -1,83 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import changeLanguage from '../../i18n/i18nChangeLanguage.js';
// About
export default {
id: 'language',
title_i18n_key: 'language',
icon: 'language.svg',
html: () => {
let h = `<h1>${i18n('language')}</h1>`;
// search
h += `<div class="search-container" style="margin-bottom: 10px;">
<input type="text" class="search search-language" placeholder="${i18n('search')}">
</div>`;
// list of languages
const available_languages = window.listSupportedLanguages();
h += '<div class="language-list">';
for ( let lang of available_languages ) {
h += `<div class="language-item ${window.locale === lang.code ? 'active' : ''}" data-lang="${lang.code}" data-english-name="${html_encode(lang.english_name)}">${html_encode(lang.name)}<img class="checkmark" src="${window.icons['checkmark.svg']}"></div>`;
}
h += '</div>';
return h;
},
init: ($el_window) => {
$el_window.on('click', '.language-item', function () {
const $this = $(this);
const lang = $this.attr('data-lang');
changeLanguage(lang);
$this.siblings().removeClass('active');
$this.addClass('active');
// make sure all other language items are visible
$this.closest('.language-list').find('.language-item').show();
});
$el_window.on('input', '.search-language', function () {
const $this = $(this);
const search = $this.val().toLowerCase();
const $container = $this.closest('.settings').find('.settings-content-container');
const $content = $container.find('.settings-content.active');
const $list = $content.find('.language-list');
const $items = $list.find('.language-item');
$items.each(function () {
const $item = $(this);
const lang = $item.attr('data-lang');
const name = $item.text().toLowerCase();
const english_name = $item.attr('data-english-name').toLowerCase();
if ( name.includes(search) || lang.includes(search) || english_name.includes(search) ) {
$item.show();
} else {
$item.hide();
}
});
});
},
on_show: ($content) => {
// Focus on search
$content.find('.search').first().focus();
// make sure all language items are visible
$content.find('.language-item').show();
// empty search
$content.find('.search').val('');
},
};
@@ -1,79 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import UIWindowThemeDialog from '../UIWindowThemeDialog.js';
import UIWindowDesktopBGSettings from '../UIWindowDesktopBGSettings.js';
// About
export default {
id: 'personalization',
title_i18n_key: 'personalization',
icon: 'palette-outline.svg',
html: () => {
return `
<h1>${i18n('personalization')}</h1>
<div class="settings-card">
<strong>${i18n('background')}</strong>
<div style="flex-grow:1;">
<button class="button change-background" style="float:right;">${i18n('change')}</button>
</div>
</div>
<div class="settings-card">
<strong>${i18n('ui_colors')}</strong>
<div style="flex-grow:1;">
<button class="button change-ui-colors" style="float:right;">${i18n('change')}</button>
</div>
</div>
<div class="settings-card">
<strong style="flex-grow:1;">${i18n('clock_visibility')}</strong>
<select class="change-clock-visible" style="margin-left: 10px; max-width: 300px;">
<option value="auto">${i18n('clock_visible_auto')}</option>
<option value="hide">${i18n('clock_visible_hide')}</option>
<option value="show">${i18n('clock_visible_show')}</option>
</select>
</div>
`;
},
init: ($el_window) => {
$el_window.find('.change-ui-colors').on('click', function (e) {
UIWindowThemeDialog({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.find('.change-background').on('click', function (e) {
UIWindowDesktopBGSettings({
window_options: {
parent_uuid: $el_window.attr('data-element_uuid'),
disable_parent_window: true,
parent_center: true,
},
});
});
$el_window.on('change', 'select.change-clock-visible', function (e) {
window.change_clock_visible(this.value);
});
window.change_clock_visible();
},
};
-96
View File
@@ -1,96 +0,0 @@
/*
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import UIWindow2FASetup from '../UIWindow2FASetup.js';
import UIWindowDisable2FA from './UIWindowDisable2FA.js';
export default {
id: 'security',
title_i18n_key: 'security',
icon: 'shield.svg',
html: () => {
let h = `<h1>${i18n('security')}</h1>`;
let user = window.user;
// change password button
if ( ! user.is_temp ) {
h += '<div class="settings-card">';
h += `<strong>${i18n('password')}</strong>`;
h += '<div style="flex-grow:1;">';
h += `<button class="button change-password" style="float:right;">${i18n('change_password')}</button>`;
h += '</div>';
h += '</div>';
}
// session manager
h += '<div class="settings-card">';
h += `<strong>${i18n('sessions')}</strong>`;
h += '<div style="flex-grow:1;">';
h += `<button class="button manage-sessions" style="float:right;">${i18n('manage_sessions')}</button>`;
h += '</div>';
h += '</div>';
// configure 2FA
if ( !user.is_temp && user.email_confirmed ) {
h += `<div class="settings-card settings-card-security ${user.otp ? 'settings-card-success' : 'settings-card-warning'}">`;
h += '<div>';
h += `<strong style="display:block;">${i18n('two_factor')}</strong>`;
h += `<span class="user-otp-state" style="display:block; margin-top:5px;">${
i18n(user.otp ? 'two_factor_enabled' : 'two_factor_disabled')
}</span>`;
h += '</div>';
h += '<div style="flex-grow:1;">';
h += `<button class="button enable-2fa" style="float:right;${user.otp ? 'display:none;' : ''}">${i18n('enable_2fa')}</button>`;
h += `<button class="button disable-2fa" style="float:right;${user.otp ? '' : 'display:none;'}">${i18n('disable_2fa')}</button>`;
h += '</div>';
h += '</div>';
}
return h;
},
init: ($el_window) => {
$el_window.find('.enable-2fa').on('click', async function (e) {
const { promise } = await UIWindow2FASetup();
const tfa_was_enabled = await promise;
if ( tfa_was_enabled ) {
$el_window.find('.enable-2fa').hide();
$el_window.find('.disable-2fa').show();
$el_window.find('.user-otp-state').text(i18n('two_factor_enabled'));
$el_window.find('.settings-card-security').removeClass('settings-card-warning');
$el_window.find('.settings-card-security').addClass('settings-card-success');
}
return;
});
$el_window.find('.disable-2fa').on('click', async function (e) {
const { promise } = await UIWindowDisable2FA();
const tfa_was_disabled = await promise;
if ( tfa_was_disabled ) {
$el_window.find('.enable-2fa').show();
$el_window.find('.disable-2fa').hide();
$el_window.find('.user-otp-state').text(i18n('two_factor_disabled'));
$el_window.find('.settings-card-security').removeClass('settings-card-success');
$el_window.find('.settings-card-security').addClass('settings-card-warning');
}
});
},
};
-191
View File
@@ -1,191 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
// Usage
export default {
id: 'usage',
title_i18n_key: 'usage',
icon: 'speedometer-outline.svg',
html: () => {
return `
<h1>${i18n('usage')}<button class="update-usage-details" style="float:right;"><svg class="update-usage-details-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/> <path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/> </svg></button></h1>
<div class="driver-usage">
<div class="driver-usage-header">
<h3 style="margin:0; font-size: 14px; flex-grow: 1; font-weight: 500;">${i18n('Storage')}</h3>
<div style="font-size: 13px; margin-bottom: 3px; opacity:0.85;">
<span id="storage-used"></span>
<span> used of </span>
<span id="storage-capacity"></span>
<span id="storage-puter-used-w" style="display:none;">&nbsp;(<span id="storage-puter-used"></span> ${i18n('storage_puter_used')})</span>
</div>
</div>
<div id="storage-bar-wrapper">
<span id="storage-used-percent"></span>
<div id="storage-bar"></div>
<div id="storage-bar-host"></div>
</div>
<div class="driver-usage-container" style="margin-top: 30px;">
<div class="driver-usage-header">
<h3 style="margin:0; font-size: 14px; flex-grow: 1; font-weight: 500;">${i18n('Resources')}</h3>
<div style="font-size: 13px; margin-bottom: 3px; opacity:0.85;">
<span id="total-usage"></span>
<span> used of </span>
<span id="total-capacity"></span>
</div>
</div>
<div class="usage-progbar-wrapper">
<div class="usage-progbar" style="width: 0;">
<span class="usage-progbar-percent"></span>
</div>
</div>
<div class="driver-usage-details" style="display:none; margin-top: 5px; font-size: 13px; cursor: pointer;">
<div class="caret" style="float:left;"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-right-fill" viewBox="0 0 16 16"><path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/></svg></div>
<span class="driver-usage-details-text disable-user-select">View usage details</span>
</div>
<div class="driver-usage-details-content hide-scrollbar" style="display: none;">
</div>
</div>
</div>`;
},
init: ($el_window) => {
update_usage_details($el_window);
$($el_window).find('.update-usage-details').on('click', function () {
update_usage_details($el_window);
});
// Scoped click handler for usage details toggle
$($el_window).on('click', '.driver-usage-details', function () {
const $container = $(this).closest('.driver-usage');
$container.find('.driver-usage-details-content').toggleClass('active');
$(this).toggleClass('active');
// change the text of the driver-usage-details-text depending on the class
if ( $(this).hasClass('active') ) {
$(this).find('.driver-usage-details-text').text('Hide usage details');
} else {
$(this).find('.driver-usage-details-text').text('View usage details');
}
});
},
};
async function update_usage_details ($el_window) {
// Add spinning animation and record start time
const startTime = Date.now();
$($el_window).find('.update-usage-details-icon').css('animation', 'spin 1s linear infinite');
const monthlyUsagePromise = puter.auth.getMonthlyUsage().then(res => {
let monthlyAllowance = res.allowanceInfo?.monthUsageAllowance;
let remaining = res.allowanceInfo?.remaining;
let totalUsage = monthlyAllowance - remaining;
let totalUsagePercentage = (totalUsage / monthlyAllowance * 100).toFixed(0);
$('#total-usage').html(window.number_format(totalUsage / 100_000_000, { decimals: 2, prefix: '$' }));
$('#total-capacity').html(window.number_format(monthlyAllowance / 100_000_000, { decimals: 2, prefix: '$' }));
$('.usage-progbar-percent').html(`${totalUsagePercentage }%`);
$('.usage-progbar').css('width', `${totalUsagePercentage }%`);
// build the table for the usage details
let h = '<table class="driver-usage-details-content-table">';
h += `<thead>
<tr>
<th>Resource</th>
<th>Units</th>
<th>Cost</th>
</tr>
</thead>`;
h += '<tbody>';
for ( let key in res.usage ) {
// value must be object
if ( typeof res.usage[key] !== 'object' )
{
continue;
}
// get the units
let units = res.usage[key].units;
// Bytes should be formatted as human readable
if ( key.startsWith('filesystem:') && key.endsWith(':bytes') ) {
units = window.byte_format(units);
}
// Everything else should be formatted as a number
else {
units = window.number_format(units, { decimals: 0, thousandSeparator: ',' });
}
h += `
<tr>
<td>${key}</td>
<td>${units}</td>
<td>${window.number_format(res.usage[key].cost / 100_000_000, { decimals: 2, prefix: '$' })}</td>
</tr>`;
}
h += '</tbody>';
h += '</table>';
$('.driver-usage-details-content').html(h);
});
const spacePromise = puter.fs.space().then(res => {
let usage_percentage = (res.used / res.capacity * 100).toFixed(0);
usage_percentage = usage_percentage > 100 ? 100 : usage_percentage;
let general_used = res.used;
let host_usage_percentage = 0;
if ( res.host_used ) {
$('#storage-puter-used').html(window.byte_format(res.used));
$('#storage-puter-used-w').show();
general_used = res.host_used;
host_usage_percentage = ((res.host_used - res.used) / res.capacity * 100).toFixed(0);
}
$('#storage-used').html(window.byte_format(general_used));
$('#storage-capacity').html(window.byte_format(res.capacity));
$('#storage-used-percent').html(
`${usage_percentage }%${
host_usage_percentage > 0
? ` / ${ host_usage_percentage }%` : ''}`);
$('#storage-bar').css('width', `${usage_percentage }%`);
$('#storage-bar-host').css('width', `${host_usage_percentage }%`);
if ( usage_percentage >= 100 ) {
$('#storage-bar').css({
'border-top-right-radius': '3px',
'border-bottom-right-radius': '3px',
});
}
});
// Wait for both promises to complete
await Promise.all([monthlyUsagePromise, spacePromise]);
// Ensure spinning continues for at least 1 second
const elapsed = Date.now() - startTime;
const minDuration = 1000; // 1 second
if ( elapsed < minDuration ) {
await new Promise(resolve => setTimeout(resolve, minDuration - elapsed));
}
// Remove spinning animation
$($el_window).find('.update-usage-details-icon').css('animation', '');
}
-195
View File
@@ -1,195 +0,0 @@
/**
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import Placeholder from '../../util/Placeholder.js';
import UIWindow from '../UIWindow.js';
def(Symbol('TSettingsTab'), 'ui.traits.TSettingsTab');
async function UIWindowSettings (options) {
return new Promise(async (resolve) => {
options = options ?? {};
const svc_settings = globalThis.services.get('settings');
const tabs = svc_settings.get_tabs();
const tab_placeholders = [];
let h = '';
h += '<div class="settings-container">';
h += '<div class="settings">';
// sidebar toggle
h += '<button class="sidebar-toggle hidden-lg hidden-xl hidden-md"><div class="sidebar-toggle-button"><span></span><span></span><span></span></div></button>';
// sidebar
h += '<div class="settings-sidebar disable-user-select disable-context-menu">';
// if data-is_fullpage="1" show title saying "Settings"
if ( options.window_options?.is_fullpage ) {
h += `<div class="settings-sidebar-title">${i18n('settings')}</div>`;
}
// sidebar items
h += `<div class="settings-sidebar-burger disable-context-menu disable-user-select" style="background-image: url(${window.icons['menu']});"></div>`;
tabs.forEach((tab, i) => {
h += `<div class="settings-sidebar-item disable-context-menu disable-user-select ${i === 0 ? 'active' : ''}" data-settings="${tab.id}" style="background-image: url(${window.icons[tab.icon]});">${i18n(tab.title_i18n_key)}</div>`;
});
h += '</div>';
// content
h += '<div class="settings-content-container">';
tabs.forEach((tab, i) => {
h += `<div class="settings-content ${i === 0 ? 'active' : ''}" data-settings="${tab.id}">`;
if ( tab.factory || tab.dom ) {
tab_placeholders[i] = Placeholder();
h += tab_placeholders[i].html;
} else {
h += tab.html();
}
h += '</div>';
});
h += '</div>';
h += '</div>';
h += '</div>';
const el_window = await UIWindow({
title: 'Settings',
app: 'settings',
single_instance: true,
icon: null,
uid: null,
is_dir: false,
body_content: h,
has_head: true,
selectable_body: false,
allow_context_menu: false,
is_resizable: true,
is_droppable: false,
init_center: true,
allow_native_ctxmenu: true,
allow_user_select: true,
backdrop: false,
width: 800,
height: 'auto',
dominant: true,
show_in_taskbar: false,
draggable_body: false,
onAppend: function (this_window) {
// send event settings-window-opened
window.dispatchEvent(new CustomEvent('settings-window-opened', { detail: { window: this_window } }));
},
window_class: 'window-settings',
body_css: {
width: 'initial',
height: '100%',
overflow: 'auto',
},
...options?.window_options ?? {},
});
const $el_window = $(el_window);
tabs.forEach((tab, i) => {
tab.init && tab.init($el_window);
if ( tab.factory ) {
const component = tab.factory();
component.attach(tab_placeholders[i]);
}
if ( tab.reinitialize ) {
tab.reinitialize();
}
if ( tab.dom ) {
tab_placeholders[i].replaceWith(tab.dom);
}
});
// If options.tab is provided, open that tab
if ( options.tab ) {
const $tabToOpen = $el_window.find(`.settings-sidebar-item[data-settings="${options.tab}"]`);
if ( $tabToOpen.length > 0 ) {
setTimeout(() => {
$tabToOpen.trigger('click');
}, 50);
}
}
$(el_window).on('click', '.settings-sidebar-item', function () {
const $this = $(this);
const settings = $this.attr('data-settings');
const $container = $this.closest('.settings').find('.settings-content-container');
const $content = $container.find(`.settings-content[data-settings="${settings}"]`);
// add active class to sidebar item
$this.siblings().removeClass('active');
$this.addClass('active');
// add active class to content
$container.find('.settings-content').removeClass('active');
$content.addClass('active');
// Run on_show handlers
const tab = tabs.find((tab) => tab.id === settings);
if ( tab?.on_show ) {
tab.on_show($content);
}
});
resolve(el_window);
});
}
$(document).on('mousedown', '.sidebar-toggle', function (e) {
e.preventDefault();
$('.settings-sidebar').toggleClass('active');
$('.sidebar-toggle-button').toggleClass('active');
// move sidebar toggle button
setTimeout(() => {
$('.sidebar-toggle').css({
left: $('.settings-sidebar').hasClass('active') ? 243 : 2,
});
}, 10);
});
$(document).on('click', '.settings-sidebar-item', function (e) {
// hide sidebar
$('.settings-sidebar').removeClass('active');
// move sidebar toggle button ro the right
setTimeout(() => {
$('.sidebar-toggle').css({
left: 2,
});
}, 10);
});
// clicking anywhere on the page will close the sidebar
$(document).on('click', function (e) {
// print event target class
if ( !$(e.target).closest('.settings-sidebar').length && !$(e.target).closest('.sidebar-toggle-button').length && !$(e.target).hasClass('sidebar-toggle-button') && !$(e.target).hasClass('sidebar-toggle') ) {
$('.settings-sidebar').removeClass('active');
$('.sidebar-toggle-button').removeClass('active');
// move sidebar toggle button ro the right
setTimeout(() => {
$('.sidebar-toggle').css({
left: 2,
});
}, 10);
}
});
export default UIWindowSettings;
+3 -10
View File
@@ -35,7 +35,6 @@ import UITaskbar from './UITaskbar.js';
import new_context_menu_item from '../helpers/new_context_menu_item.js';
import refresh_item_container from '../helpers/refresh_item_container.js';
import changeLanguage from '../i18n/i18nChangeLanguage.js';
import UIWindowSettings from './Settings/UIWindowSettings.js';
import UIWindowTaskManager from './UIWindowTaskManager.js';
import truncate_filename from '../helpers/truncate_filename.js';
import UINotification from './UINotification.js';
@@ -1296,16 +1295,10 @@ async function UIDesktop (options) {
}
}
//--------------------------------------------------------------------------------------
// /settings will open settings in fullpage mode
// /settings redirects to /dashboard
//--------------------------------------------------------------------------------------
else if ( window.url_paths[0]?.toLocaleLowerCase() === 'settings' ) {
// open settings
UIWindowSettings({
tab: window.url_paths[1] || 'about',
window_options: {
is_fullpage: true,
},
});
window.open('/dashboard', '_blank');
}
// ---------------------------------------------
// Run apps from insta-login URL
@@ -2201,7 +2194,7 @@ $(document).on('click', '.user-options-menu-btn', async function (e) {
html: i18n('keyboard_shortcuts'),
id: 'keyboard_shortcuts',
onClick: async function () {
UIWindowSettings({ tab: 'keyboard-shortcuts' });
window.open('/dashboard', '_blank');
},
},
//--------------------------------------------------
-2
View File
@@ -48,7 +48,6 @@ import { IPCService } from './services/IPCService.js';
import { LaunchOnInitService } from './services/LaunchOnInitService.js';
import { LocaleService } from './services/LocaleService.js';
import { ProcessService } from './services/ProcessService.js';
import { SettingsService } from './services/SettingsService.js';
import { ThemeService } from './services/ThemeService.js';
import { privacy_aware_path } from './util/desktop.js';
@@ -92,7 +91,6 @@ const launch_services = async function (options) {
register('theme', new ThemeService());
register('process', new ProcessService());
register('locale', new LocaleService());
register('settings', new SettingsService());
register('anti-csrf', new AntiCSRFService());
register('__launch-on-init', new LaunchOnInitService());
+1 -2
View File
@@ -19,7 +19,6 @@
import UIAlert from './UI/UIAlert.js';
import UIWindowSearch from './UI/UIWindowSearch.js';
import UIWindowSettings from './UI/Settings/UIWindowSettings.js';
import launch_app from './helpers/launch_app.js';
import open_item from './helpers/open_item.js';
import determine_active_container_parent from './helpers/determine_active_container_parent.js';
@@ -33,7 +32,7 @@ $(document).bind('keydown', async function (e) {
if ( e.which === 112 || ((e.ctrlKey || e.metaKey) && e.shiftKey && e.which === 191) ) {
e.preventDefault();
e.stopPropagation();
UIWindowSettings({ tab: 'keyboard-shortcuts' });
window.open('/dashboard', '_blank');
return false;
}
//-----------------------------------------------------------------------------
-64
View File
@@ -1,64 +0,0 @@
/*
* Copyright (C) 2024-present 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 <https://www.gnu.org/licenses/>.
*/
import { Service } from '../definitions.js';
import AboutTab from '../UI/Settings/UITabAbout.js';
import UsageTab from '../UI/Settings/UITabUsage.js';
import AccountTab from '../UI/Settings/UITabAccount.js';
import SecurityTab from '../UI/Settings/UITabSecurity.js';
import PersonalizationTab from '../UI/Settings/UITabPersonalization.js';
import LanguageTag from '../UI/Settings/UITabLanguage.js';
import KeyboardShortcutsTab from '../UI/Settings/UITabKeyboardShortcuts.js';
import UIElement from '../UI/UIElement.js';
const TSettingsTab = use('ui.traits.TSettingsTab');
export class SettingsService extends Service {
#tabs = [];
async _init () {
;[
UsageTab,
AccountTab,
SecurityTab,
PersonalizationTab,
LanguageTag,
KeyboardShortcutsTab,
AboutTab,
].forEach(tab => {
this.register_tab(tab);
});
}
get_tabs () {
return this.#tabs;
}
register_tab (tab) {
if ( tab instanceof UIElement ) {
const ui_element = tab;
tab = {
...ui_element.as(TSettingsTab).get_metadata(),
reinitialize () {
ui_element.reinitialize();
},
get dom () {
return ui_element.root;
},
};
}
this.#tabs.push(tab);
}
}