buildChatGptUrl and its fitToBudget helpers have had no production caller since prepareAction took over routing; the 2026-10-05 plan kept them only for existing tests until then. They also shortened silently, against the ask-before-shortening rule, so they were a trap to reuse. Port the tests that guard live code onto applyTemplate and serializePrompt, including the $&, $`, $' and $$ regressions (checked to fail against the string-replacement form), and cover prepareAction's whole-word cut, which nothing tested before. Also drop getContext's inputType password check. menus.OnClickData has no inputType, so it never fired; Firefox already withholds password fields from selectionText via mozIsTextField(true) in SelectionUtils (bug 565717). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.9 KiB
JavaScript
50 lines
1.9 KiB
JavaScript
// Pure URL construction. No browser APIs, so node:test imports this directly.
|
|
//
|
|
// Every ChatGPT parameter below is undocumented by OpenAI and has broken before.
|
|
// This module's contract is only that it emits a well-formed URL; whether
|
|
// ChatGPT honours temporary-chat / hints is out of our control, which is
|
|
// why each is independently omittable and every label is hint-strength.
|
|
//
|
|
// The root path is mandatory: chatgpt.com/search began returning 404 in
|
|
// mid-2025, and the root form is what OpenAI's own extension uses.
|
|
|
|
const BASE = "https://chatgpt.com/";
|
|
|
|
export const MAX_URL_CHARS = 8000;
|
|
|
|
export function applyTemplate(template, query) {
|
|
if (query === "") {
|
|
return "";
|
|
}
|
|
const tpl = typeof template === "string" ? template : "";
|
|
if (tpl.includes("{query}")) {
|
|
// Replacer function, not a replacement string: String.prototype.replaceAll
|
|
// gives $&, $`, $', and $$ special meaning in a replacement STRING, and
|
|
// `query` is arbitrary page-selected text that can contain any of those
|
|
// (shell one-liners routinely do). The function form disables that
|
|
// interpretation entirely. Do not "simplify" this back to the string form.
|
|
return tpl.replaceAll("{query}", () => query);
|
|
}
|
|
const trimmed = tpl.trim();
|
|
return trimmed === "" ? query : `${trimmed} ${query}`;
|
|
}
|
|
|
|
function addOptionalParams(url, settings) {
|
|
if (settings.temporaryChat) {
|
|
url.searchParams.set("temporary-chat", "true");
|
|
}
|
|
if (settings.webSearch) {
|
|
url.searchParams.set("hints", "search");
|
|
}
|
|
return url;
|
|
}
|
|
|
|
// Encode a complete prompt without silently shortening it; prepareAction owns
|
|
// the length budget and asks before shortening. q goes first, matching the
|
|
// most-cited working examples.
|
|
export function serializePrompt(settings, prompt) {
|
|
const url = new URL(BASE);
|
|
if (prompt) url.searchParams.set("q", prompt);
|
|
return addOptionalParams(url, settings).href;
|
|
}
|