feat(notes): commandes masquees avec l'oeil des notes

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 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-10-10 18:35:37 +01:00
parent bbd4e2ef06
commit 6ca1e4b2cf
5 changed files with 37 additions and 3 deletions
+5
View File
@@ -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 liste (`qsCommands`), la saisie filtre, Entree **tape sans Entree** et
**sans Ctrl+A + Suppr** (`executeAutofill(..., noClear)` : dans un terminal **sans Ctrl+A + Suppr** (`executeAutofill(..., noClear)` : dans un terminal
Ctrl+A ramene en debut de ligne), Ctrl+Entree copie, Esc revient aux entrees. 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 - **Settings search** : `#settingsSearch` filtre les `.setting-row` par
texte ; match sur le label de section garde toute la section. Esc dans texte ; match sur le label de section garde toute la section. Esc dans
la search avec query = clear ; Esc vide = ferme Settings. la search avec query = clear ; Esc vide = ferme Settings.
+18 -1
View File
@@ -4050,6 +4050,20 @@ function parseCommandNote(text) {
return out; 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 // HISTORIQUE DES ENTREES OUVERTES, comme celui d'un navigateur. Fonctions
// pures sur {ids, pos} : testables sans DOM (tests/history.test.js). // pures sur {ids, pos} : testables sans DOM (tests/history.test.js).
const ENTRY_HISTORY_MAX = 50; const ENTRY_HISTORY_MAX = 50;
@@ -4671,7 +4685,8 @@ function soCommandsField() {
if (c.group) list.appendChild(el('div', { class: 'so-cmds-group' }, c.group)); if (c.group) list.appendChild(el('div', { class: 'so-cmds-group' }, c.group));
} }
const row = el('div', { class: 'so-cmds-row' }); 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' }); const b = el('button', { class: 'icon-btn icon-btn-sm', type: 'button', title: 'Copy command' });
b.appendChild(icon('i-copy')); b.appendChild(icon('i-copy'));
b.addEventListener('click', async ev => { b.addEventListener('click', async ev => {
@@ -4721,6 +4736,8 @@ function soNoteBodyField(value) {
state.maskNotes = !state.maskNotes; state.maskNotes = !state.maskNotes;
localStorage.setItem('maskNotes', state.maskNotes ? '1' : '0'); localStorage.setItem('maskNotes', state.maskNotes ? '1' : '0');
ta.classList.toggle('is-masked', state.maskNotes); ta.classList.toggle('is-masked', state.maskNotes);
// La liste des commandes suit l'oeil (elle ecoute 'input').
ta.dispatchEvent(new Event('input'));
saveServerSettings(); saveServerSettings();
}); });
wrap.appendChild(ta); wrap.appendChild(ta);
+2 -1
View File
@@ -99,7 +99,8 @@ function qsRenderCommands(box, q) {
'data-cmd': String(x.i), 'data-cmd': String(x.i),
}); });
const main = el('div', { class: 'quick-search-main' }); 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)); if (x.c.group) main.appendChild(el('div', { class: 'quick-search-sub' }, x.c.group));
row.appendChild(main); row.appendChild(main);
const acts = el('div', { class: 'quick-search-acts' }); const acts = el('div', { class: 'quick-search-acts' });
+11
View File
@@ -25,6 +25,17 @@ test('"## titre" et "#" seul', () => {
assert.equal(r[1].group, ''); 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', () => { test('texte vide ou absent', () => {
assert.equal(T.parseCommandNote('').length, 0); assert.equal(T.parseCommandNote('').length, 0);
assert.equal(T.parseCommandNote(null).length, 0); assert.equal(T.parseCommandNote(null).length, 0);
+1 -1
View File
@@ -159,7 +159,7 @@ function loadApp(overrides = {}) {
// historique des entrees ouvertes // historique des entrees ouvertes
entryHistoryPush, entryHistoryTarget, ENTRY_HISTORY_MAX, entryHistoryPush, entryHistoryTarget, ENTRY_HISTORY_MAX,
// note de commandes // note de commandes
parseCommandNote, parseCommandNote, maskCommand,
}; };
`; `;