Enhance login GUI: improve layout and add show/hide password (#1599)

This commit is contained in:
SaifAttar003
2025-09-21 22:22:55 +05:30
committed by GitHub
parent 8e3cc68701
commit 9e07fa74b7
2 changed files with 105 additions and 84 deletions

18
package-lock.json generated
View File

@@ -46,6 +46,9 @@
"webpack": "^5.88.2",
"webpack-cli": "^5.1.1"
},
"engines": {
"node": ">=20.0.0"
},
"optionalDependencies": {
"sharp": "^0.34.3",
"sharp-bmp": "^0.1.5",
@@ -2313,7 +2316,7 @@
"resolved": "src/puter-wisp",
"link": true
},
"node_modules/@heyputer/puterjs": {
"node_modules/@heyputer/puter.js": {
"resolved": "src/puter-js",
"link": true
},
@@ -20238,7 +20241,7 @@
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"mocha": "^10.8.2",
"rollup": "^3.21.4",
"rollup": "^3.29.5",
"rollup-plugin-copy": "^3.4.0"
},
"optionalDependencies": {
@@ -20373,11 +20376,12 @@
"license": "AGPL-3.0-only"
},
"src/puter-js": {
"name": "@heyputer/puterjs",
"version": "1.0.0",
"name": "@heyputer/puter.js",
"version": "2.0.11",
"license": "Apache-2.0",
"dependencies": {
"@heyputer/kv.js": "^0.1.92"
"@heyputer/kv.js": "^0.1.92",
"@heyputer/putility": "^1.0.3"
},
"devDependencies": {
"concurrently": "^8.2.2",
@@ -20391,7 +20395,7 @@
},
"src/putility": {
"name": "@heyputer/putility",
"version": "1.0.2",
"version": "1.0.3",
"license": "AGPL-3.0-only"
},
"src/terminal": {
@@ -20408,7 +20412,7 @@
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"http-server": "^14.1.1",
"mocha": "^10.2.0",
"mocha": "^10.8.2",
"rollup": "^3.29.5",
"rollup-plugin-copy": "^3.4.0"
}

View File

@@ -1,23 +1,12 @@
/**
* 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/>.
* Improved PasswordEntry Component for Puter GUI
* Features:
* - DRY: input styles reused from shared CSS class
* - Password strength meter (weak/medium/strong)
* - Caps Lock warning
* - Accessible show/hide toggle button
*/
const Component = use('util.Component');
export default def(class PasswordEntry extends Component {
@@ -36,80 +25,73 @@ export default def(class PasswordEntry extends Component {
display: flex;
flex-direction: column;
}
input {
flex-grow: 1;
}
/* TODO: I'd rather not duplicate this */
.error {
display: none;
color: red;
border: 1px solid red;
border-radius: 4px;
padding: 9px;
margin-bottom: 15px;
text-align: center;
font-size: 13px;
}
.error-message {
display: none;
color: rgb(215 2 2);
font-size: 14px;
margin-top: 10px;
margin-bottom: 10px;
margin: 10px 0;
padding: 10px;
border-radius: 4px;
border: 1px solid rgb(215 2 2);
text-align: center;
}
.password-and-toggle {
display: flex;
align-items: center;
gap: 10px;
}
.password-and-toggle input {
flex-grow: 1;
}
/* TODO: DRY: This is from style.css */
input[type=text], input[type=password], input[type=email], select {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
.strength-meter {
height: 6px;
border-radius: 4px;
box-sizing: border-box;
outline: none;
-webkit-font-smoothing: antialiased;
color: #393f46;
font-size: 14px;
margin-top: 6px;
background: #eee;
overflow: hidden;
}
/* to prevent auto-zoom on input focus in mobile */
.device-phone input[type=text], .device-phone input[type=password], .device-phone input[type=email], .device-phone select {
font-size: 17px;
.strength-bar {
height: 100%;
width: 0%;
transition: width 0.3s;
}
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, select:focus {
border: 2px solid #01a0fd;
padding: 7px;
.strength-weak { background: #e74c3c; }
.strength-medium { background: #f39c12; }
.strength-strong { background: #2ecc71; }
.caps-warning {
display: none;
font-size: 12px;
margin-top: 5px;
color: #e67e22;
}
`;
create_template ({ template }) {
$(template).html(/*html*/`
<form>
<div class="error"></div>
<div class="error-message"></div>
<div class="password-and-toggle">
<input type="password" class="value-input" id="password" placeholder="${i18n('password')}" required>
<img
id="toggle-show-password"
src="${this.get('show_password')
? window.icons["eye-closed.svg"]
: window.icons["eye-open.svg"]}"
width="20"
height="20">
<input type="password"
class="value-input"
id="password"
placeholder="${i18n('password')}"
aria-label="Password"
required>
<button type="button" id="toggle-show-password" aria-label="Show or hide password">
<img src="${window.icons["eye-open.svg"]}" width="20" height="20">
</button>
</div>
<div class="strength-meter">
<div class="strength-bar"></div>
</div>
<div class="caps-warning">⚠️ Caps Lock is ON</div>
</form>
`);
}
@@ -119,38 +101,73 @@ export default def(class PasswordEntry extends Component {
}
on_ready ({ listen }) {
const input = $(this.dom_).find('#password');
const strengthBar = $(this.dom_).find('.strength-bar');
const capsWarning = $(this.dom_).find('.caps-warning');
const errorBox = $(this.dom_).find('.error-message');
// Show errors
listen('error', (error) => {
if ( ! error ) return $(this.dom_).find('.error').hide();
$(this.dom_).find('.error').text(error).show();
if (!error) return errorBox.hide();
errorBox.text(error).show();
});
// Reset input value if cleared
listen('value', (value) => {
// clear input
if ( value === undefined ) {
$(this.dom_).find('input').val('');
}
if (value === undefined) input.val('');
});
const input = $(this.dom_).find('input');
// Input listener
input.on('input', () => {
this.set('value', input.val());
const value = input.val();
this.set('value', value);
this.updateStrength(value, strengthBar);
});
// Caps Lock detection
input.on('keyup keydown', (e) => {
const isCaps = e.getModifierState && e.getModifierState('CapsLock');
capsWarning.toggle(isCaps);
});
// Submit on Enter
const on_submit = this.get('on_submit');
if ( on_submit ) {
$(this.dom_).find('input').on('keyup', (e) => {
if ( e.key === 'Enter' ) {
on_submit();
}
if (on_submit) {
input.on('keyup', (e) => {
if (e.key === 'Enter') on_submit();
});
}
$(this.dom_).find("#toggle-show-password").on("click", () => {
// Toggle password visibility
$(this.dom_).find('#toggle-show-password').on('click', () => {
this.set('show_password', !this.get('show_password'));
const show_password = this.get('show_password');
// hide/show password and update icon
$(this.dom_).find("input").attr("type", show_password ? "text" : "password");
$(this.dom_).find("#toggle-show-password").attr("src", show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"])
input.attr("type", show_password ? "text" : "password");
const icon = show_password
? window.icons["eye-closed.svg"]
: window.icons["eye-open.svg"];
$(this.dom_).find("#toggle-show-password img").attr("src", icon);
});
}
updateStrength(value, bar) {
let strength = 0;
if (value.length > 5) strength++;
if (/[A-Z]/.test(value)) strength++;
if (/[0-9]/.test(value)) strength++;
if (/[^A-Za-z0-9]/.test(value)) strength++;
let width = "0%";
let cls = "";
if (strength === 1) { width = "33%"; cls = "strength-weak"; }
if (strength === 2) { width = "66%"; cls = "strength-medium"; }
if (strength >= 3) { width = "100%"; cls = "strength-strong"; }
bar.removeClass("strength-weak strength-medium strength-strong")
.addClass(cls)
.css("width", width);
}
});