fix: cap huge selections before shortening

The shortcut and toolbar read window.getSelection() without a limit, so a
select-all on a very large page handed prepareAction millions of
characters. Every binary-search probe re-encoded the whole candidate
prefix: about 1.1 s for 5M characters, paid again by the dialog.

Each code point adds at least one character to the URL, so nothing past
the first MAX_URL_CHARS can fit. Cap there before any encoding. A capped
source never takes the ready path: renderPrompt trims, so a prefix of
mostly whitespace could fit and silently drop the rest without consent.

Output is unchanged: 2,000 random cases matched develop exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
golem
2026-10-18 01:32:22 -06:00
co-authored by Claude Opus 5.5
parent 99bb58f7b7
commit 519131c21f
3 changed files with 36 additions and 3 deletions
+14 -3
View File
@@ -25,12 +25,23 @@ export function prepareAction(request) {
}
const resolved = { ...request, language };
const serialize = text => serializePrompt(request.settings, renderPrompt(resolved, text));
const url = serialize(source);
if (url.length <= MAX_URL_CHARS) return { kind: 'ready', url };
// Each code point adds at least one URL character, so none past the first
// MAX_URL_CHARS can fit. Capping first keeps a select-all on a huge page from
// costing seconds of re-encoding. A capped source is never sent as ready: its
// kept prefix may trim down to something that fits and silently drop the rest.
const points = [];
let capped = false;
for (const point of source) {
if (points.length === MAX_URL_CHARS) { capped = true; break; }
points.push(point);
}
if (!capped) {
const url = serialize(source);
if (url.length <= MAX_URL_CHARS) return { kind: 'ready', url };
}
if (isUrl) return error('This URL is too long to send intact. No part of it has been sent.');
const marker = '\n[Selected text shortened]';
if (serialize(marker).length >= MAX_URL_CHARS) return error('The prompt instructions are too long. Shorten your prompt template and try again.');
const points = Array.from(source);
let low = 0, high = points.length;
while (low < high) {
const mid = Math.ceil((low + high) / 2);