From ede2dd135bcf700608ce53ad6a1a26aad650fc08 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Mon, 3 Aug 2026 09:19:40 -0500 Subject: [PATCH] feat: add data-birb-target, cap scale, persist sticky note size - Add [data-birb-target] to default focusable elements so site owners can mark buttons, cards, and other elements as valid perch targets (closes #19 partially, addresses sites without prominent images) - Cap birb scale multiplier at 5x to prevent the bird from growing so large it covers the entire UI (closes #25) - Persist sticky note width/height via ResizeObserver so notes maintain their size across page reloads (closes #29) --- src/application.js | 2 +- src/context.js | 2 +- src/stickyNotes.js | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/application.js b/src/application.js index 9f4b794..fa5217d 100644 --- a/src/application.js +++ b/src/application.js @@ -304,7 +304,7 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { } else { newMultiplier = currentMultiplier + 1; } - newMultiplier = Math.max(0.25, Math.round(newMultiplier * 4) / 4); + newMultiplier = Math.max(0.25, Math.min(5, Math.round(newMultiplier * 4) / 4)); userSettings.birbScaleMultiplier = newMultiplier; save(); updateBirbScale(); diff --git a/src/context.js b/src/context.js index ce3654b..c5f7e9a 100644 --- a/src/context.js +++ b/src/context.js @@ -42,7 +42,7 @@ export class Context { * @returns {string[]} A list of CSS selectors for focusable elements */ getFocusableElements() { - return ["img", "video", ".birb-sticky-note"]; + return ["img", "video", ".birb-sticky-note", "[data-birb-target]"]; } getFocusElementTopMargin() { diff --git a/src/stickyNotes.js b/src/stickyNotes.js index 77224eb..f133424 100644 --- a/src/stickyNotes.js +++ b/src/stickyNotes.js @@ -12,6 +12,8 @@ import { * @property {string} content * @property {number} top * @property {number} left + * @property {number} [width] + * @property {number} [height] */ export class StickyNote { @@ -21,13 +23,17 @@ export class StickyNote { * @param {string} [content] * @param {number} [top] * @param {number} [left] + * @param {number} [width] + * @param {number} [height] */ - constructor(id, site = "", content = "", top = 0, left = 0) { + constructor(id, site = "", content = "", top = 0, left = 0, width, height) { this.id = id; this.site = site; this.content = content; this.top = top; this.left = left; + this.width = width; + this.height = height; } } @@ -56,7 +62,8 @@ export function renderStickyNote(stickyNote, page, onSave, onDelete) { const content = makeElement("birb-window-content"); const textarea = document.createElement("textarea"); textarea.className = "birb-sticky-note-input"; - textarea.style.width = "150px"; + textarea.style.width = stickyNote.width ? `${stickyNote.width}px` : "150px"; + textarea.style.height = stickyNote.height ? `${stickyNote.height}px` : ""; textarea.placeholder = "Write your notes here and they'll stick to the page!"; textarea.value = stickyNote.content; content.appendChild(textarea); @@ -96,6 +103,21 @@ export function renderStickyNote(stickyNote, page, onSave, onDelete) { onSave(); }, 250); }); + + // Persist size on resize + /** @type {ReturnType|undefined} */ + let resizeTimeout; + const resizeObserver = new ResizeObserver(() => { + stickyNote.width = textarea.offsetWidth; + stickyNote.height = textarea.offsetHeight; + if (resizeTimeout) { + clearTimeout(resizeTimeout); + } + resizeTimeout = setTimeout(() => { + onSave(); + }, 250); + }); + resizeObserver.observe(textarea); } // On window resize