From 9e07fa74b77893a51c2d1d2000ad2275080acd1d Mon Sep 17 00:00:00 2001
From: SaifAttar003 <168440311+SaifAttar003@users.noreply.github.com>
Date: Sun, 21 Sep 2025 22:22:55 +0530
Subject: [PATCH] Enhance login GUI: improve layout and add show/hide password
(#1599)
---
package-lock.json | 18 ++-
src/gui/src/UI/Components/PasswordEntry.js | 171 +++++++++++----------
2 files changed, 105 insertions(+), 84 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 5c69d128..17e1548a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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"
}
diff --git a/src/gui/src/UI/Components/PasswordEntry.js b/src/gui/src/UI/Components/PasswordEntry.js
index 90ecaf93..da5e3ee1 100644
--- a/src/gui/src/UI/Components/PasswordEntry.js
+++ b/src/gui/src/UI/Components/PasswordEntry.js
@@ -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 .
+ * 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*/`
`);
}
@@ -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);
+ }
});