From ab92046f71d5ccdfcad9f2c37d37fd02fa2a4788 Mon Sep 17 00:00:00 2001 From: David Conger Date: Sun, 14 Jun 2026 17:55:32 -0700 Subject: [PATCH 1/3] Auto-expand Note field up to 7 lines; floor manual resize at 1 line The clipper Note field grows as the user types, up to a maximum of 7 lines, after which it scrolls. Manual drag-resize (resize: vertical) is preserved: the dragged height is remembered as a floor, so the field never auto-shrinks below it, but typing still grows it to fit content (up to the max). This means collapsing the field and then adding more text re-expands it to an appropriate size. A CSS min-height keeps manual resize from going below a single line (previously it could shrink smaller). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/renderer.html | 2 +- src/scripts/renderer.ts | 47 ++++++++++++++++++++++++++++++++++++++++ src/styles/renderer.less | 4 ++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/renderer.html b/src/renderer.html index cdabadf1..7e77f21c 100644 --- a/src/renderer.html +++ b/src/renderer.html @@ -103,7 +103,7 @@

OneNote Web Clipper

- +
diff --git a/src/scripts/renderer.ts b/src/scripts/renderer.ts index 8f0f468c..0dfc2946 100644 --- a/src/scripts/renderer.ts +++ b/src/scripts/renderer.ts @@ -366,6 +366,52 @@ document.querySelectorAll(".mode-btn").forEach((btn) => { }); titleField.placeholder = strings.titlePlaceholder; noteField.placeholder = strings.notePlaceholder; + +// --- Note field auto-grow ------------------------------------------------ +// Grows the Note field with its content up to NOTE_MAX_LINES, then scrolls. +// A manual drag-resize is remembered as a floor so typing can still grow the +// field but never auto-shrinks it below the height the user chose. +const NOTE_LINE_HEIGHT = 20; // px — must match line-height in renderer.less +const NOTE_VERTICAL_CHROME = 10; // 8px padding (4+4) + 2px border (1+1) +const NOTE_MAX_LINES = 7; +const NOTE_MAX_HEIGHT = NOTE_MAX_LINES * NOTE_LINE_HEIGHT + NOTE_VERTICAL_CHROME; +let userPreferredNoteHeight = 0; // px; 0 = no manual preference yet +let lastAutoNoteHeight = 0; + +function autoGrowNoteField() { + noteField.style.height = "auto"; + // (offsetHeight - clientHeight) is the exact vertical border to add back under + // border-box; it also tracks the focus border change. + let borderY = noteField.offsetHeight - noteField.clientHeight; + let contentHeight = noteField.scrollHeight + borderY; + let fitHeight = Math.min(contentHeight, NOTE_MAX_HEIGHT); + let targetHeight = Math.max(fitHeight, userPreferredNoteHeight); + noteField.style.height = targetHeight + "px"; + noteField.style.overflowY = contentHeight > targetHeight ? "auto" : "hidden"; + lastAutoNoteHeight = noteField.offsetHeight; +} + +function resetNoteFieldSize() { + userPreferredNoteHeight = 0; + autoGrowNoteField(); +} + +noteField.addEventListener("input", autoGrowNoteField); +// Detect a drag-resize: it starts on the field and ends with a mouseup at a +// height that no longer matches our last auto value. Record it as the floor (the +// next keystroke grows past it as needed); the mousedown gate avoids stray clicks. +let noteResizeInProgress = false; +noteField.addEventListener("mousedown", () => { noteResizeInProgress = true; }); +document.addEventListener("mouseup", () => { + if (!noteResizeInProgress) { return; } + noteResizeInProgress = false; + if (Math.abs(noteField.offsetHeight - lastAutoNoteHeight) > 1) { + userPreferredNoteHeight = noteField.offsetHeight; + lastAutoNoteHeight = noteField.offsetHeight; + noteField.style.overflowY = "auto"; + } +}); +autoGrowNoteField(); // Mode button tooltips (matches old modeButton.tsx tooltip pattern) let tooltipMap: any = { fullpage: loc("WebClipper.ClipType.ScreenShot.Button.Tooltip", "Take a screenshot of the whole page, just like you see it."), @@ -3313,6 +3359,7 @@ port.onMessage.addListener((message: any) => { // Reset metadata fields titleField.value = ""; noteField.value = ""; + resetNoteFieldSize(); sourceUrlText.textContent = ""; sourceUrl.title = ""; // Clear stale capture content from DOM diff --git a/src/styles/renderer.less b/src/styles/renderer.less index 6126ee4c..d51122db 100644 --- a/src/styles/renderer.less +++ b/src/styles/renderer.less @@ -340,6 +340,10 @@ iframe#preview-frame { // Title is a single-line input per design #title-field { resize: none; } +// Floor manual drag-resize at one line (auto-grow handled in renderer.ts): +// 20px line-height + 8px padding + 2px border = 30px. +#note-field { min-height: 30px; } + .source-url { font-size: 14px; line-height: 20px; color: @colorNeutralForeground3; From 0cba87e9f32c6e999db965f7c409f4a1d9a46f1b Mon Sep 17 00:00:00 2001 From: David Conger Date: Tue, 16 Jun 2026 18:35:52 -0700 Subject: [PATCH 2/3] Derive Note field max-height from computed style Addresses PR review: replace hard-coded NOTE_LINE_HEIGHT/NOTE_VERTICAL_CHROME with values read from getComputedStyle(noteField), so the 7-line cap stays correct if the CSS line-height, padding, or border ever change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/scripts/renderer.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/scripts/renderer.ts b/src/scripts/renderer.ts index 0dfc2946..a0d751b5 100644 --- a/src/scripts/renderer.ts +++ b/src/scripts/renderer.ts @@ -371,20 +371,27 @@ noteField.placeholder = strings.notePlaceholder; // Grows the Note field with its content up to NOTE_MAX_LINES, then scrolls. // A manual drag-resize is remembered as a floor so typing can still grow the // field but never auto-shrinks it below the height the user chose. -const NOTE_LINE_HEIGHT = 20; // px — must match line-height in renderer.less -const NOTE_VERTICAL_CHROME = 10; // 8px padding (4+4) + 2px border (1+1) const NOTE_MAX_LINES = 7; -const NOTE_MAX_HEIGHT = NOTE_MAX_LINES * NOTE_LINE_HEIGHT + NOTE_VERTICAL_CHROME; let userPreferredNoteHeight = 0; // px; 0 = no manual preference yet let lastAutoNoteHeight = 0; +// Derive the N-line cap from the field's live computed style so it stays correct +// if the CSS line-height, padding, or border ever change (incl. theming). +function getNoteMaxHeight() { + let cs = getComputedStyle(noteField); + let lineHeight = parseFloat(cs.lineHeight) || 20; + let verticalChrome = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom) + + parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth); + return NOTE_MAX_LINES * lineHeight + verticalChrome; +} + function autoGrowNoteField() { noteField.style.height = "auto"; // (offsetHeight - clientHeight) is the exact vertical border to add back under // border-box; it also tracks the focus border change. let borderY = noteField.offsetHeight - noteField.clientHeight; let contentHeight = noteField.scrollHeight + borderY; - let fitHeight = Math.min(contentHeight, NOTE_MAX_HEIGHT); + let fitHeight = Math.min(contentHeight, getNoteMaxHeight()); let targetHeight = Math.max(fitHeight, userPreferredNoteHeight); noteField.style.height = targetHeight + "px"; noteField.style.overflowY = contentHeight > targetHeight ? "auto" : "hidden"; From ba8975f11779dd97cdcb19d14f7a3f0a754ba47e Mon Sep 17 00:00:00 2001 From: David Conger Date: Wed, 17 Jun 2026 19:22:26 -0700 Subject: [PATCH 3/3] Clamp Note-field manual-resize floor to the 7-line cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address @wikirby's review on #663: the manual drag-resize floor was stored unclamped (userPreferredNoteHeight = noteField.offsetHeight), which produced three bugs once a drag exceeded the 7-line auto-grow cap: - dragging past 7 lines stuck forever (inflated floor persisted across typing), - couldn't drag back below the cap with >7 lines of text, - the scrollbar disappeared after a tall drag (overflowY keyed off the floor). Fix (both suggested changes): - renderer.ts: clamp the remembered floor to the cap on mouseup — userPreferredNoteHeight = Math.min(noteField.offsetHeight, getNoteMaxHeight()). - renderer.less: add max-height: 150px to #note-field (7 x 20px line-height + 8px padding + 2px border) so the native resize handle can't physically exceed the cap even before JS runs; mirrors NOTE_MAX_LINES. With the floor clamped to the cap, overflowY keys off content vs. cap and the scrollbar returns. Build + lint pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/scripts/renderer.ts | 5 ++++- src/styles/renderer.less | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/scripts/renderer.ts b/src/scripts/renderer.ts index a0d751b5..67ac3c01 100644 --- a/src/scripts/renderer.ts +++ b/src/scripts/renderer.ts @@ -407,13 +407,16 @@ noteField.addEventListener("input", autoGrowNoteField); // Detect a drag-resize: it starts on the field and ends with a mouseup at a // height that no longer matches our last auto value. Record it as the floor (the // next keystroke grows past it as needed); the mousedown gate avoids stray clicks. +// The floor is clamped to the auto-grow cap so a manual drag can never persist a +// height taller than NOTE_MAX_LINES (which would otherwise stick across later +// typing and suppress the scrollbar). let noteResizeInProgress = false; noteField.addEventListener("mousedown", () => { noteResizeInProgress = true; }); document.addEventListener("mouseup", () => { if (!noteResizeInProgress) { return; } noteResizeInProgress = false; if (Math.abs(noteField.offsetHeight - lastAutoNoteHeight) > 1) { - userPreferredNoteHeight = noteField.offsetHeight; + userPreferredNoteHeight = Math.min(noteField.offsetHeight, getNoteMaxHeight()); lastAutoNoteHeight = noteField.offsetHeight; noteField.style.overflowY = "auto"; } diff --git a/src/styles/renderer.less b/src/styles/renderer.less index d51122db..bba4a659 100644 --- a/src/styles/renderer.less +++ b/src/styles/renderer.less @@ -340,9 +340,11 @@ iframe#preview-frame { // Title is a single-line input per design #title-field { resize: none; } -// Floor manual drag-resize at one line (auto-grow handled in renderer.ts): -// 20px line-height + 8px padding + 2px border = 30px. -#note-field { min-height: 30px; } +// Note field manual-resize bounds (auto-grow handled in renderer.ts). min-height +// floors a drag at one line; max-height stops the native resize handle from +// dragging past the 7-line cap (mirrors NOTE_MAX_LINES in renderer.ts). +// One line = 20px line-height + 8px padding + 2px border = 30px; 7 lines = 150px. +#note-field { min-height: 30px; max-height: 150px; } .source-url { font-size: 14px; line-height: 20px;