From f23e81ccbb34735ed79f4e99ba44ec9f2185225b Mon Sep 17 00:00:00 2001 From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 09:25:25 -0400 Subject: [PATCH] fix(stories): avoid lone surrogate when trimming story to 1500 chars Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- scripts/story-dialog.test.mjs | 8 ++++++++ stories/index.html | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/story-dialog.test.mjs b/scripts/story-dialog.test.mjs index 2e3d704..fcf9841 100644 --- a/scripts/story-dialog.test.mjs +++ b/scripts/story-dialog.test.mjs @@ -306,6 +306,14 @@ test("story over 1500 chars is trimmed in the form URL and the user is told", () assert.match(t.el("story-status").textContent, /^Your story was trimmed to 1500 characters/); }); +test("story cut inside an emoji drops the half pair so the form URL still opens", () => { + const t = boot(); + t.fill({ ...VALID, "story-text": "a".repeat(STORY_MAX - 1) + "😀 tail" }); + t.submit(); + assert.equal(t.opened.length, 1, "submit opens the draft issue instead of throwing URIError"); + assert.equal(t.issueUrl().searchParams.get("story"), "a".repeat(STORY_MAX - 1)); +}); + test("story at exactly 1500 chars is not trimmed", () => { const t = boot(); t.fill({ ...VALID, "story-text": "y".repeat(STORY_MAX) }); diff --git a/stories/index.html b/stories/index.html index ffd359d..4ebcc5d 100644 --- a/stories/index.html +++ b/stories/index.html @@ -2,7 +2,7 @@ - + Contributor Stories — Hive Commons @@ -378,7 +378,10 @@

Open a contributor story draft

function truncateStory(story) { if (story.length <= STORY_MAX) return { text: story, truncated: false }; - return { text: story.slice(0, STORY_MAX).trimEnd(), truncated: true }; + var cut = story.slice(0, STORY_MAX); + // A cut inside a surrogate pair leaves a lone surrogate that encodeURIComponent rejects. + if (/[\uD800-\uDBFF]$/.test(cut)) cut = cut.slice(0, -1); + return { text: cut.trimEnd(), truncated: true }; } function markdown(data) {