From 6ca1e4b2cf1cde2530a35d003d6f402f88847f59 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:35:37 +0100 Subject: [PATCH] feat(notes): commandes masquees avec l'oeil des notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Partage d'ecran : premier mot et 3 derniers caracteres visibles (ssh ••••.11), dans le panneau et la recherche rapide. Filtre, copie et remplissage gardent le vrai texte. Aide de la recherche rapide adaptee aux commandes. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 5 +++++ js/app.js | 19 ++++++++++++++++++- js/app.overlays.js | 3 ++- js/tests/commands.test.js | 11 +++++++++++ js/tests/harness.js | 2 +- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9468b17..e346987 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1582,6 +1582,11 @@ après chaque `DELETE /attachments/{id}`. Un petit vault sain ne paie rien. liste (`qsCommands`), la saisie filtre, Entree **tape sans Entree** et **sans Ctrl+A + Suppr** (`executeAutofill(..., noClear)` : dans un terminal Ctrl+A ramene en debut de ligne), Ctrl+Entree copie, Esc revient aux entrees. + **Masquage** (partage d'ecran) sur l'oeil existant des notes (`maskNotes`) : + `maskCommand` garde le premier mot et les 3 derniers caracteres + (`ssh ••••••••••••.11`), une fin de 6 caracteres ou moins est masquee en + entier. Affichage seulement : filtre, copie et remplissage voient le vrai + texte. L'oeil re-declenche `input` sur le textarea pour redessiner la liste. - **Settings search** : `#settingsSearch` filtre les `.setting-row` par texte ; match sur le label de section garde toute la section. Esc dans la search avec query = clear ; Esc vide = ferme Settings. diff --git a/js/app.js b/js/app.js index 2116e30..05374e0 100644 --- a/js/app.js +++ b/js/app.js @@ -4050,6 +4050,20 @@ function parseCommandNote(text) { return out; } +// AFFICHAGE MASQUE d'une commande (partage d'ecran), sur l'oeil "mask notes" : +// le premier mot reste, puis le milieu masque, puis les 3 derniers caracteres. +// "ssh admin@10.0.0.11" -> "ssh ••••••••••••.11". Une fin trop courte pour +// en garder 3 sans tout montrer est masquee en entier. Affichage seulement : +// filtre, copie et remplissage travaillent sur le vrai texte. +function maskCommand(cmd) { + const s = String(cmd || ''); + const sp = s.indexOf(' '); + const head = sp < 0 ? '' : s.slice(0, sp + 1); + const rest = sp < 0 ? s : s.slice(sp + 1); + if (rest.length <= 6) return head + '•'.repeat(rest.length); + return head + '•'.repeat(rest.length - 3) + rest.slice(-3); +} + // HISTORIQUE DES ENTREES OUVERTES, comme celui d'un navigateur. Fonctions // pures sur {ids, pos} : testables sans DOM (tests/history.test.js). const ENTRY_HISTORY_MAX = 50; @@ -4671,7 +4685,8 @@ function soCommandsField() { if (c.group) list.appendChild(el('div', { class: 'so-cmds-group' }, c.group)); } const row = el('div', { class: 'so-cmds-row' }); - row.appendChild(el('code', { class: 'so-cmds-cmd' }, c.cmd)); + row.appendChild(el('code', { class: 'so-cmds-cmd' }, + state.maskNotes ? maskCommand(c.cmd) : c.cmd)); const b = el('button', { class: 'icon-btn icon-btn-sm', type: 'button', title: 'Copy command' }); b.appendChild(icon('i-copy')); b.addEventListener('click', async ev => { @@ -4721,6 +4736,8 @@ function soNoteBodyField(value) { state.maskNotes = !state.maskNotes; localStorage.setItem('maskNotes', state.maskNotes ? '1' : '0'); ta.classList.toggle('is-masked', state.maskNotes); + // La liste des commandes suit l'oeil (elle ecoute 'input'). + ta.dispatchEvent(new Event('input')); saveServerSettings(); }); wrap.appendChild(ta); diff --git a/js/app.overlays.js b/js/app.overlays.js index 710ea74..1473972 100644 --- a/js/app.overlays.js +++ b/js/app.overlays.js @@ -99,7 +99,8 @@ function qsRenderCommands(box, q) { 'data-cmd': String(x.i), }); const main = el('div', { class: 'quick-search-main' }); - main.appendChild(el('div', { class: 'quick-search-name qs-cmd' }, x.c.cmd)); + main.appendChild(el('div', { class: 'quick-search-name qs-cmd' }, + state.maskNotes ? maskCommand(x.c.cmd) : x.c.cmd)); if (x.c.group) main.appendChild(el('div', { class: 'quick-search-sub' }, x.c.group)); row.appendChild(main); const acts = el('div', { class: 'quick-search-acts' }); diff --git a/js/tests/commands.test.js b/js/tests/commands.test.js index 0972cb8..8680cfe 100644 --- a/js/tests/commands.test.js +++ b/js/tests/commands.test.js @@ -25,6 +25,17 @@ test('"## titre" et "#" seul', () => { assert.equal(r[1].group, ''); }); +test('masque : premier mot, milieu masque, 3 derniers caracteres', () => { + assert.equal(T.maskCommand('ssh admin@10.0.0.11'), 'ssh ' + '•'.repeat(12) + '.11'); + assert.equal(T.maskCommand('psql -h 10.0.0.20'), 'psql ' + '•'.repeat(9) + '.20'); +}); + +test('masque : fin courte masquee en entier, pas d\'espace', () => { + assert.equal(T.maskCommand('ls -la'), 'ls •••'); + assert.equal(T.maskCommand('hostname'), '•••••ame'); + assert.equal(T.maskCommand(''), ''); +}); + test('texte vide ou absent', () => { assert.equal(T.parseCommandNote('').length, 0); assert.equal(T.parseCommandNote(null).length, 0); diff --git a/js/tests/harness.js b/js/tests/harness.js index 618c4fd..3a69607 100644 --- a/js/tests/harness.js +++ b/js/tests/harness.js @@ -159,7 +159,7 @@ function loadApp(overrides = {}) { // historique des entrees ouvertes entryHistoryPush, entryHistoryTarget, ENTRY_HISTORY_MAX, // note de commandes - parseCommandNote, + parseCommandNote, maskCommand, }; `;