From 9dc9f625d62b59cc45c615b23a40767441a8d34a Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:31:26 +0100 Subject: [PATCH] fix(entries): size the multiline field from its content instead of a fixed height A manual resize was lost on reopen: the row is rebuilt on every open, and remembering the height would not survive a restart either since localStorage is wiped when the ephemeral port changes the origin. Deriving it from the content removes the state entirely - the field is always the right size. --- TEST_SCENARIOS.md | 2 +- css/style.css | 3 ++- js/app.js | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/TEST_SCENARIOS.md b/TEST_SCENARIOS.md index e0dcb26..84c37f0 100644 --- a/TEST_SCENARIOS.md +++ b/TEST_SCENARIOS.md @@ -78,7 +78,7 @@ Préparer : 3 entrées `A`, `B`, `C`, dont `A` dans le dossier `Work` avec le ta ### A12 · pas de fausse alerte (non-régression d'A11) Aucun de ces enchaînements ne doit afficher « Entry changed elsewhere » : -- **A12a** : `A` ouverte + dirty → Save → modifier encore → Save (le rechargement post-save re-cale la référence) +- **A12a** : `A` ouverte + dirty → Save → modifier encore → Save (le rechargement post-save re-cale la référence)✅ - **A12b** : `A` ouverte + dirty → batch *Add tag* / *Move to folder* sur `A` → Save (l'éditeur a absorbé le changement) - **A12c** : `A` ouverte + dirty → supprimer son dossier → Save (cf. A7) - **A12d** : `A` ouverte + dirty → *Fetch missing* favicons → Save (l'icône est hors payload éditeur) diff --git a/css/style.css b/css/style.css index 260e9b5..f70b8c5 100644 --- a/css/style.css +++ b/css/style.css @@ -1604,11 +1604,12 @@ input[type="range"]::-webkit-slider-thumb { /* Multiline custom field (Notes). The row stays a grid, so the textarea just grows in its own column and the buttons keep their places. */ .so-custom-multiline { - min-height: 72px; + min-height: 56px; resize: vertical; align-self: stretch; font-family: inherit; line-height: 1.5; + overflow-y: auto; /* JS caps the auto-grow; past that it scrolls */ } /* Editable combobox for option-backed custom fields (card brand, expiry year…). Input fills the cell; the arrow drops a menu of ALL options. */ diff --git a/js/app.js b/js/app.js index 258ae35..5a2c137 100644 --- a/js/app.js +++ b/js/app.js @@ -4161,6 +4161,12 @@ function soCustomFieldsField() { return wrap; } +// Grow a textarea to fit its content, up to a cap past which it scrolls. +function autoSizeTextarea(ta, maxPx = 320) { + ta.style.height = 'auto'; + ta.style.height = Math.min(ta.scrollHeight, maxPx) + 'px'; +} + function buildCustomFieldRow(field, idx, rerender) { const row = el('div', { class: 'so-custom-row' }); const labelInput = el('input', { @@ -4189,10 +4195,18 @@ function buildCustomFieldRow(field, idx, rerender) { if (field.multiline) { valueInput = el('textarea', { class: 'so-input so-custom-value so-custom-multiline', - rows: 5, placeholder: 'Value', + rows: 3, placeholder: 'Value', autocomplete: 'off', autocorrect: 'off', spellcheck: 'false', }); valueInput.classList.toggle('is-masked', !!field.is_secret); + // Height follows the content instead of being remembered: a manual + // resize would be lost anyway (re-rendered on every open, and + // localStorage is wiped each launch when the port changes). + const grow = () => autoSizeTextarea(valueInput); + valueInput.addEventListener('input', grow); + // scrollHeight reads 0 until the element is in the DOM — same + // render-time vs DOM-attachment trap as renderSoChips (CLAUDE.md). + setTimeout(grow, 0); } else { valueInput = el('input', { type: field.is_secret ? 'password' : 'text',