Merge pull request 'fix: limit the prompt template to 1,000 characters' (#12) from fix/template-length-limit into develop
Reviewed-on: https://git.zavage.net/Zavage-Software/search-with-chatgpt-powered-by-openai-extension/pulls/12
This commit is contained in:
@@ -74,6 +74,7 @@ Open `about:addons`, select this extension, and choose the **Options** tab
|
|||||||
guarantee.
|
guarantee.
|
||||||
- **Open in** — a new tab, a new background tab, or a new window.
|
- **Open in** — a new tab, a new background tab, or a new window.
|
||||||
- **Prompt template** — wraps your text, for example `Explain simply: {query}`.
|
- **Prompt template** — wraps your text, for example `Explain simply: {query}`.
|
||||||
|
Up to 1,000 characters.
|
||||||
|
|
||||||
Restore defaults resets extension preferences to Simple mode with translation
|
Restore defaults resets extension preferences to Simple mode with translation
|
||||||
hidden; it does not change Firefox's search engine or shortcut settings.
|
hidden; it does not change Firefox's search engine or shortcut settings.
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
export const OPEN_IN_VALUES = ["new-tab", "background-tab", "new-window"];
|
export const OPEN_IN_VALUES = ["new-tab", "background-tab", "new-window"];
|
||||||
|
|
||||||
|
// In UTF-16 units, as maxlength counts. Keeps the template far inside
|
||||||
|
// storage.sync's 8192-byte item quota even when every character is a pasted
|
||||||
|
// control character (six bytes once JSON-escaped), and far beyond any template
|
||||||
|
// the 8,000-character URL budget could send.
|
||||||
|
export const MAX_TEMPLATE_CHARS = 1000;
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS = {
|
export const DEFAULT_SETTINGS = {
|
||||||
contextMenuMode: "simple",
|
contextMenuMode: "simple",
|
||||||
showTranslate: false,
|
showTranslate: false,
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_SETTINGS, OPEN_IN_VALUES } from "./defaults.js";
|
import { DEFAULT_SETTINGS, MAX_TEMPLATE_CHARS, OPEN_IN_VALUES } from "./defaults.js";
|
||||||
|
|
||||||
export function normalizeLanguage(value) {
|
export function normalizeLanguage(value) {
|
||||||
if (typeof value !== "string") return "";
|
if (typeof value !== "string") return "";
|
||||||
@@ -28,7 +28,7 @@ export function mergeSettings(stored) {
|
|||||||
if (OPEN_IN_VALUES.includes(stored.openIn)) {
|
if (OPEN_IN_VALUES.includes(stored.openIn)) {
|
||||||
merged.openIn = stored.openIn;
|
merged.openIn = stored.openIn;
|
||||||
}
|
}
|
||||||
if (typeof stored.promptTemplate === "string") {
|
if (typeof stored.promptTemplate === "string" && stored.promptTemplate.length <= MAX_TEMPLATE_CHARS) {
|
||||||
merged.promptTemplate = stored.promptTemplate;
|
merged.promptTemplate = stored.promptTemplate;
|
||||||
}
|
}
|
||||||
return merged;
|
return merged;
|
||||||
|
|||||||
@@ -80,8 +80,8 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="promptTemplate">Prompt template</label>
|
<label for="promptTemplate">Prompt template</label>
|
||||||
<input type="text" id="promptTemplate" spellcheck="false" />
|
<input type="text" id="promptTemplate" spellcheck="false" maxlength="1000" />
|
||||||
<p class="hint"><code>{query}</code> is replaced with your selected text. Used by Ask normally, Simple mode, the toolbar button, and the keyboard shortcut. Other advanced actions use their own prompts.</p>
|
<p class="hint"><code>{query}</code> is replaced with your selected text. Used by Ask normally, Simple mode, the toolbar button, and the keyboard shortcut. Other advanced actions use their own prompts. Up to 1,000 characters.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
|||||||
+27
-1
@@ -1,7 +1,9 @@
|
|||||||
import test from "node:test";
|
import test from "node:test";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
import { DEFAULT_SETTINGS } from "../lib/defaults.js";
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
import { DEFAULT_SETTINGS, MAX_TEMPLATE_CHARS } from "../lib/defaults.js";
|
||||||
import { mergeSettings } from "../lib/settings.js";
|
import { mergeSettings } from "../lib/settings.js";
|
||||||
|
|
||||||
test("empty object yields the defaults", () => {
|
test("empty object yields the defaults", () => {
|
||||||
@@ -67,6 +69,30 @@ test("wrong types fall back to the default", () => {
|
|||||||
assert.equal(merged.promptTemplate, DEFAULT_SETTINGS.promptTemplate);
|
assert.equal(merged.promptTemplate, DEFAULT_SETTINGS.promptTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a prompt template over the length limit falls back to the default", () => {
|
||||||
|
assert.equal(typeof MAX_TEMPLATE_CHARS, "number");
|
||||||
|
const atLimit = "x".repeat(MAX_TEMPLATE_CHARS);
|
||||||
|
assert.equal(mergeSettings({ promptTemplate: atLimit }).promptTemplate, atLimit);
|
||||||
|
assert.equal(mergeSettings({ promptTemplate: `${atLimit}x` }).promptTemplate,
|
||||||
|
DEFAULT_SETTINGS.promptTemplate);
|
||||||
|
});
|
||||||
|
|
||||||
|
// storage.sync rejects any item over 8192 bytes (key plus JSON value), and the
|
||||||
|
// options page writes every field in one set(), so one oversized template would
|
||||||
|
// make every later save fail. The worst character is a pasted control
|
||||||
|
// character, which JSON escapes to six bytes.
|
||||||
|
test("a template at the limit fits storage.sync's per-item quota in the worst case", () => {
|
||||||
|
const worst = "\u0001".repeat(MAX_TEMPLATE_CHARS);
|
||||||
|
const bytes = new TextEncoder().encode("promptTemplate" + JSON.stringify(worst)).length;
|
||||||
|
assert.ok(bytes <= 8192, `${bytes} bytes`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("the options page enforces the same template limit", () => {
|
||||||
|
const html = readFileSync(new URL("../options/options.html", import.meta.url), "utf8");
|
||||||
|
const input = html.match(/<input[^>]*id="promptTemplate"[^>]*>/)?.[0] ?? "";
|
||||||
|
assert.equal(input.match(/maxlength="(\d+)"/)?.[1], String(MAX_TEMPLATE_CHARS));
|
||||||
|
});
|
||||||
|
|
||||||
test("an out-of-range openIn falls back to the default", () => {
|
test("an out-of-range openIn falls back to the default", () => {
|
||||||
assert.equal(mergeSettings({ openIn: "teleport" }).openIn, DEFAULT_SETTINGS.openIn);
|
assert.equal(mergeSettings({ openIn: "teleport" }).openIn, DEFAULT_SETTINGS.openIn);
|
||||||
assert.equal(mergeSettings({ openIn: "" }).openIn, DEFAULT_SETTINGS.openIn);
|
assert.equal(mergeSettings({ openIn: "" }).openIn, DEFAULT_SETTINGS.openIn);
|
||||||
|
|||||||
Reference in New Issue
Block a user