···11import { API_PARAMS, API_TIMEOUT_MS } from "./config";
22import type { ApiErrorResponse, ApiChatCompletionStreamChunk } from "./types/api";
3344-/** Error thrown when the API call fails for any reason (network, HTTP, malformed response). */
44+/** error thrown when the api call fails for any reason (network, http, malformed response). */
55export class ApiError extends Error {
66 constructor(
77 message: string,
···1414}
15151616/**
1717- * Streams a text correction request to the local llama.cpp server.
1717+ * streams a text correction request to the local llama.cpp server.
1818 *
1919- * Yields each token as it arrives from the SSE stream, enabling progressive
2020- * display in the UI without waiting for the full response.
1919+ * yields each token as it arrives from the sse stream, enabling progressive
2020+ * display in the ui without waiting for the full response.
2121 *
2222- * @param text - Validated, non-empty input text
2323- * @param systemPrompt - System prompt to instruct the model
2424- * @param baseUrl - API server base URL (e.g. "http://localhost:8080")
2525- * @yields Individual content tokens from the model's stream
2626- * @throws {@link ApiError} on timeout, HTTP errors, or network failures
2222+ * @param text - validated, non-empty input text
2323+ * @param systemPrompt - system prompt to instruct the model
2424+ * @param baseUrl - api server base url (e.g. "http://localhost:8080")
2525+ * @yields individual content tokens from the model's stream
2626+ * @throws {@link ApiError} on timeout, http errors, or network failures
2727 */
2828export async function* streamCorrection(
2929 text: string,
···6565 );
6666 }
67676868- // Connection established — clear the connect-timeout
6868+ /* connection established — clear the connect-timeout */
6969 clearTimeout(timeout);
70707171- // ─── HTTP status errors ──────────────────────────────────────────────────
7171+ /* http status errors */
72727373 if (!response.ok) {
7474 const status = response.status;
···103103 }
104104 }
105105106106- // ─── SSE stream parsing ─────────────────────────────────────────────────
106106+ /* sse stream parsing */
107107108108 const body = response.body;
109109 if (!body) {
+20-26
src/background.ts
···22import { validateInput, ValidationError } from "./validation";
33import { CORRECT_PROMPT, SUGGEST_PROMPT, API_BASE_URL, STORAGE_KEY_API_URL } from "./config";
4455-/** Context menu item ID. */
55+/** context menu item id */
66const MENU_ID = "correct-with-llamacpp";
7788-/** Section identifiers for streaming responses. */
88+/** section identifiers for streaming responses */
99type Section = "corrected" | "suggested";
10101111-/** Maps a section to its system prompt. */
1111+/** maps a section to its system prompt */
1212const SECTION_PROMPTS: Record<Section, string> = {
1313 corrected: CORRECT_PROMPT,
1414 suggested: SUGGEST_PROMPT,
1515};
16161717-/**
1818- * Pending state for a popup window that hasn't signalled "ready" yet.
1919- */
1717+/** pending state for a popup window that hasn't signalled "ready" yet */
2018interface PendingResult {
2119 tabId: number;
2220 resolve: () => void;
···24222523let pending: PendingResult | null = null;
26242727-// ─── Storage helpers ───────────────────────────────────────────────────────
2525+/* storage helpers */
28262929-/** Reads the configured API base URL from storage, falling back to the default. */
2727+/** reads the configured api base url from storage, falling back to the default */
3028async function getApiBaseUrl(): Promise<string> {
3129 const result = await browser.storage.local.get(STORAGE_KEY_API_URL);
3230 const stored = result[STORAGE_KEY_API_URL];
3331 return typeof stored === "string" && stored.length > 0 ? stored : API_BASE_URL;
3432}
35333636-// ─── Context menu setup ────────────────────────────────────────────────────
3434+/* context menu setup */
37353836browser.runtime.onInstalled.addListener(() => {
3937 browser.contextMenus.create({
···4341 });
4442});
45434646-// ─── Message handler (result tab readiness + retry) ────────────────────────
4444+/* message handler (result tab readiness + retry) */
47454846browser.runtime.onMessage.addListener((msg: { type: string }, sender) => {
4947 if (msg.type === "ready") {
···6361 }
6462});
65636666-// ─── Context menu click handler ────────────────────────────────────────────
6464+/* context menu click handler */
67656866browser.contextMenus.onClicked.addListener(async (info) => {
6967 if (info.menuItemId !== MENU_ID) {
7068 return;
7169 }
72707373- // 1. Validate input
7171+ /* 1. validate input */
7472 let inputText: string;
7573 try {
7674 inputText = validateInput(info.selectionText);
···8078 return;
8179 }
82808383- // 2. Open popup immediately (shows loading spinner)
8181+ /* 2. open popup immediately (shows loading spinner) */
8482 const window = await browser.windows.create({
8583 type: "popup",
8684 url: browser.runtime.getURL("result.html"),
···9492 }
9593 const tabId = tab.id;
96949797- // 3. Wait for the result tab to signal "ready"
9595+ /* 3. wait for the result tab to signal "ready" */
9896 const readyPromise = new Promise<void>((resolve) => {
9997 pending = { tabId, resolve };
10098 });
10199 await readyPromise;
102100 pending = null;
103101104104- // 4. Tell the result tab to show the original text
102102+ /* 4. tell the result tab to show the original text */
105103 await browser.tabs.sendMessage(tabId, {
106104 type: "start",
107105 original: inputText,
108106 });
109107110110- // 5. Sequential streaming with per-section error handling
108108+ /* 5. sequential streaming with per-section error handling */
111109 const baseUrl = await getApiBaseUrl();
112110113111 const correctedOk = await attemptStreamSection(tabId, inputText, "corrected", baseUrl);
···119117 await browser.tabs.sendMessage(tabId, { type: "done" });
120118});
121119122122-// ─── Stream a single section, returning success/failure ────────────────────
123123-124120/**
125125- * Attempts to stream a section. On success, sends `section-done`.
126126- * On failure, sends `section-error` with the error message.
127127- * Returns `true` if the section completed successfully.
121121+ * attempts to stream a section. on success sends section-done.
122122+ * on failure sends section-error with the error message.
123123+ * returns true if the section completed successfully.
128124 */
129125async function attemptStreamSection(
130126 tabId: number,
···145141 }
146142}
147143148148-// ─── Stream a single section from the API ──────────────────────────────────
149149-144144+/** stream a single section from the api, measuring ttft latency */
150145async function streamSection(
151146 tabId: number,
152147 text: string,
···174169 await browser.tabs.sendMessage(tabId, { type: "section-done", section });
175170}
176171177177-// ─── Retry handler ─────────────────────────────────────────────────────────
172172+/* retry handler */
178173179174async function handleRetry(tabId: number, section: Section, original: string): Promise<void> {
180175 const baseUrl = await getApiBaseUrl();
181176 await attemptStreamSection(tabId, original, section, baseUrl);
182177}
183178184184-// ─── Helper: open popup with an error when validation fails immediately ────
185185-179179+/** open popup with an error when validation fails immediately */
186180async function openPopupWithError(message: string): Promise<void> {
187181 const window = await browser.windows.create({
188182 type: "popup",
+2-2
src/config.ts
···1919 * Instructs the model to return only the corrected text.
2020 */
2121export const CORRECT_PROMPT = `# Agent Guidelines
2222-You are an agent specialized in english grammar correction.
2222+You are an agent specialized in english and french grammar correction.
2323Correct the grammar, spelling, and punctuation of the submitted text.
24242525# Output
···3232 * Instructs the model to return a better-worded version of the text.
3333 */
3434export const SUGGEST_PROMPT = `# Agent Guidelines
3535-You are an agent specialized in english writing improvement.
3535+You are an agent specialized in english and french writing improvement.
3636Rewrite the submitted text with better wording and phrasing while preserving the original meaning.
37373838# Output
+14-14
src/result.ts
···11import { API_BASE_URL, STORAGE_KEY_API_URL } from "./config";
2233-// ─── DOM references ────────────────────────────────────────────────────────
33+/* dom references */
4455const apiUrlInput = document.getElementById("api-url") as HTMLInputElement;
66const apiSavedEl = document.getElementById("api-saved")!;
···1818const latencyCorrectedEl = document.getElementById("latency-corrected")!;
1919const latencySuggestedEl = document.getElementById("latency-suggested")!;
20202121-// ─── Section state ─────────────────────────────────────────────────────────
2121+/* section state */
22222323type Section = "corrected" | "suggested";
2424···5353 },
5454};
55555656-/** Stored original text for retry requests. */
5656+/** stored original text for retry requests */
5757let originalText = "";
58585959-// ─── Message types from background script ──────────────────────────────────
5959+/* message types from background script */
60606161type ResultMessage =
6262 | { type: "start"; original: string }
···6767 | { type: "done" }
6868 | { type: "error"; message: string };
69697070-// ─── Message handler ───────────────────────────────────────────────────────
7070+/* message handler */
71717272browser.runtime.onMessage.addListener((msg: ResultMessage) => {
7373 switch (msg.type) {
···9494 }
9595});
96969797-// ─── UI helpers ────────────────────────────────────────────────────────────
9797+/* ui helpers */
98989999function showStart(original: string): void {
100100 originalText = original;
···159159function showSectionError(section: Section, message: string): void {
160160 const state = sections[section];
161161162162- // Hide and detach the streaming indicator
162162+ /* hide and detach the streaming indicator */
163163 state.indicator.classList.add("hidden");
164164 state.indicator.remove();
165165···178178 errorMessageEl.textContent = message;
179179}
180180181181-// ─── Click-to-copy on content boxes ────────────────────────────────────────
181181+/* click-to-copy on content boxes */
182182183183function setupCopyOnDone(section: Section): void {
184184 const state = sections[section];
···193193 state.el.classList.add("copied");
194194 setTimeout(() => state.el.classList.remove("copied"), 2_000);
195195 } catch {
196196- // silently ignore clipboard failures
196196+ /* silently ignore clipboard failures */
197197 }
198198 });
199199}
···201201setupCopyOnDone("corrected");
202202setupCopyOnDone("suggested");
203203204204-// ─── Retry icons ───────────────────────────────────────────────────────────
204204+/* retry icons */
205205206206function setupRetryIcon(section: Section): void {
207207 const state = sections[section];
···222222setupRetryIcon("corrected");
223223setupRetryIcon("suggested");
224224225225-// ─── API URL settings ──────────────────────────────────────────────────────
225225+/* api url settings */
226226227227-/** Load stored API URL into the input field on popup open. */
227227+/** load stored api url into the input field on popup open */
228228browser.storage.local.get(STORAGE_KEY_API_URL).then((result) => {
229229 const stored = result[STORAGE_KEY_API_URL];
230230 apiUrlInput.value = typeof stored === "string" && stored.length > 0
···232232 : API_BASE_URL;
233233});
234234235235-/** Save API URL to storage on change. */
235235+/** save api url to storage on change */
236236apiUrlInput.addEventListener("change", () => {
237237 const value = apiUrlInput.value.trim();
238238 if (value.startsWith("http://") || value.startsWith("https://")) {
···243243 }
244244});
245245246246-// ─── Signal readiness to background script ─────────────────────────────────
246246+/* signal readiness to background script */
247247248248browser.runtime.sendMessage({ type: "ready" });