From 8086c818b6eab69a8a7d73043afeeed579659728 Mon Sep 17 00:00:00 2001 From: Tomas Rimkus Date: Fri, 21 Aug 2026 18:31:35 +0300 Subject: [PATCH] refactor(ui): name the head budget and the truncation suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the budget comment on #1058 by the second route the review offered -- defining the constant as the head budget rather than reserving the suffix inside it. TOAST_ERROR_BUDGET read as though it bounded `display`, but `display` is the head plus " …", so it could be 402. Renamed to HEAD_BUDGET and documented as bounding the message rather than the rendered string, with the suffix now a named constant instead of a literal in the template. Reserving the two characters was the alternative, but nothing downstream has a hard limit -- the description box scrolls -- so it would have shortened the message to satisfy a round number. Verified: head <= 400 and display <= 402 on every truncating input, including no-space text, a short first line, sentence-boundary backoff and the real 4795-char error, with the untouched-when-not-truncated and exact-`full` invariants still holding. Co-Authored-By: Claude Opus 5 (1M context) --- app/src/lib/utils/errorText.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/src/lib/utils/errorText.ts b/app/src/lib/utils/errorText.ts index 5b26b38d..1e42a69a 100644 --- a/app/src/lib/utils/errorText.ts +++ b/app/src/lib/utils/errorText.ts @@ -12,9 +12,19 @@ * budget, and report how much was dropped so nobody assumes they read it all. */ -/** Characters of an error worth putting in a toast. Roughly the first - * paragraph — enough for a sentence or two of real message. */ -const TOAST_ERROR_BUDGET = 400; +/** Longest head of an oversized error kept for the toast, before the ellipsis. + * + * Roughly the first paragraph — enough for a sentence or two of real message. + * This bounds the *message* rather than the rendered string: `display` is this + * plus `TRUNCATION_SUFFIX` when something was cut. Spending two of these + * characters on the ellipsis instead would shorten the message to no purpose, + * since nothing downstream has a hard character limit — the description box + * scrolls. + */ +const HEAD_BUDGET = 400; + +/** Marks a `display` value as incomplete. Appended after the head. */ +const TRUNCATION_SUFFIX = ' …'; /** Below this, condensing is not worth it and the whole message is shown. * @@ -25,7 +35,7 @@ const TOAST_ERROR_BUDGET = 400; * around a single character. No Copy action is offered in this range because * nothing is being withheld: `display` already holds the entire message. */ -const MIN_TO_CONDENSE = TOAST_ERROR_BUDGET + 120; +const MIN_TO_CONDENSE = HEAD_BUDGET + 120; export interface CondensedError { /** What to show in the toast. @@ -60,16 +70,16 @@ export function condenseError(raw: string | null | undefined): CondensedError { // it fits, since a newline is a stronger boundary than any punctuation. const firstLine = text.split('\n', 1)[0].trim(); let head = - firstLine.length > 0 && firstLine.length <= TOAST_ERROR_BUDGET + firstLine.length > 0 && firstLine.length <= HEAD_BUDGET ? firstLine - : text.slice(0, TOAST_ERROR_BUDGET); + : text.slice(0, HEAD_BUDGET); if (head.length < text.length && head === text.slice(0, head.length)) { // Back off to the last sentence end inside the budget so the text does not // stop mid-word. Only accept it if it keeps most of the budget — otherwise // a stray early period would throw away usable context. const lastStop = Math.max(head.lastIndexOf('. '), head.lastIndexOf('? ')); - if (lastStop > TOAST_ERROR_BUDGET * 0.4) { + if (lastStop > HEAD_BUDGET * 0.4) { head = head.slice(0, lastStop + 1); } } @@ -83,5 +93,5 @@ export function condenseError(raw: string | null | undefined): CondensedError { return { display: full, full, truncated: false, omitted: 0 }; } - return { display: `${head} …`, full, truncated: true, omitted }; + return { display: `${head}${TRUNCATION_SUFFIX}`, full, truncated: true, omitted }; }