// uiComponents.js // UI du mini gestionnaire de mots de passe. import St from 'gi://St'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js'; const Clutter = imports.gi.Clutter; const Clipboard = St.Clipboard.get_default(); const ClipboardType = St.ClipboardType.CLIPBOARD; function copyToClipboard(text) { if (text) Clipboard.set_text(ClipboardType, text); } export class PasswordEntryRow extends PopupMenu.PopupBaseMenuItem { constructor(entry) { super({ reactive: true }); const box = new St.BoxLayout({ vertical: false, x_expand: true }); let labelWidget; if (entry.url) { labelWidget = new St.Label({ text: entry.label, style_class: 'password-applet-label-link', x_expand: true }); labelWidget.clutter_text.underline = true; labelWidget.connect('button-press-event', () => { Gio.AppInfo.launch_default_for_uri(entry.url, null); }); } else { labelWidget = new St.Label({ text: entry.label, x_expand: true }); } box.add_child(labelWidget); box.add_child(new St.Label({ text: entry.username, x_expand: true })); box.add_child(new St.Label({ text: '••••••••' })); const uBtn = new St.Button({ label: 'U' }); uBtn.connect('clicked', () => copyToClipboard(entry.username)); box.add_child(uBtn); const pBtn = new St.Button({ label: 'P' }); pBtn.connect('clicked', () => copyToClipboard(entry.password_enc)); box.add_child(pBtn); this.actor.add_child(box); } } export class PasswordListSection extends PopupMenu.PopupMenuSection { setEntries(entries) { this.removeAll(); if (!entries.length) { this.addMenuItem(new PopupMenu.PopupMenuItem( 'Aucune entrée', { reactive: false } )); return; } for (const e of entries) this.addMenuItem(new PasswordEntryRow(e)); } } export class AddEntrySection extends PopupMenu.PopupMenuSection { constructor(repo, refreshCb) { super(); this._repo = repo; this._refreshCb = refreshCb; this._showAddButton(); } _showAddButton() { this.removeAll(); const item = new PopupMenu.PopupMenuItem('➕ Ajouter une entrée'); item.connect('activate', () => this._showForm()); this.addMenuItem(item); } _showForm() { this.removeAll(); const wrapper = new PopupMenu.PopupBaseMenuItem({ reactive: false }); const box = new St.BoxLayout({ vertical: true }); const mkEntry = hint => new St.Entry({ hint_text: hint, style_class: 'password-applet-entry' }); const label = mkEntry("Label"); const user = mkEntry("User"); const pwd = mkEntry("Password"); const url = mkEntry("URL"); const notes = mkEntry("Notes"); const mkRow = (name, widget) => { const row = new St.BoxLayout(); row.add_child(new St.Label({ text: name + " : " })); row.add_child(widget); return row; }; box.add_child(mkRow("Label", label)); box.add_child(mkRow("User", user)); box.add_child(mkRow("Password", pwd)); box.add_child(mkRow("URL", url)); box.add_child(mkRow("Notes", notes)); const btnRow = new St.BoxLayout(); const save = new St.Button({ label: "Enregistrer" }); save.connect('clicked', () => { const entry = { label: label.get_text().trim(), username: user.get_text().trim(), password: pwd.get_text().trim(), url: url.get_text().trim(), notes: notes.get_text().trim() }; if (!entry.label || !entry.username || !entry.password) { Main.notify("Password Applet", "Champs obligatoires manquants"); return; } this._repo.addEntry(entry); this._refreshCb(); this._showAddButton(); }); const cancel = new St.Button({ label: "Annuler" }); cancel.connect('clicked', () => this._showAddButton()); btnRow.add_child(save); btnRow.add_child(cancel); box.add_child(btnRow); wrapper.actor.add_child(box); this.addMenuItem(wrapper); } } export class PasswordApplet extends PanelMenu.Button { constructor(repo) { super(0.0, 'PasswordApplet', false); this._repo = repo; this.add_child(new St.Icon({ icon_name: 'dialog-password-symbolic', style_class: 'system-status-icon' })); this._list = new PasswordListSection(); this.menu.addMenuItem(this._list); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._add = new AddEntrySection(repo, () => this._reload()); this.menu.addMenuItem(this._add); this._reload(); } _reload() { const entries = this._repo.listEntries(); this._list.setEntries(entries); } }