Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions apps/extension/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
getLastCaptureImages,
testScrollTargets,
} from "./orchestrator";
import { loadShotCore } from "./wasm-loader";

function bytesToBase64(bytes: Uint8Array): string {
let binary = "";
Expand Down Expand Up @@ -83,6 +84,28 @@ function pngPixelSize(bytes: Uint8Array): { width: number; height: number } | nu
return { width, height };
}

/**
* Small PNG preview of a capture for the popup's thumbnail. The popup's
* preview used to ride in the capture response as full-size base64 data URLs,
* which at a long page crossed chrome.runtime.sendMessage's ~64MiB message
* cap — the response died after the capture had already succeeded, so the
* popup showed an error and no preview over a healthy capture (saves and the
* editor, which read blob-store, kept working). A ~320px-wide re-encode is a
* few KB and cannot hit the cap; the full-size bytes stay in blob-store for
* everything that actually needs them.
*/
const PREVIEW_WIDTH_PX = 320;

async function previewDataUrl(image: Uint8Array): Promise<string | undefined> {
const size = pngPixelSize(image);
if (!size) return undefined;
const shotCore = await loadShotCore();
const width = Math.min(size.width, PREVIEW_WIDTH_PX);
const height = Math.max(1, Math.round((size.height * width) / size.width));
const thumb = shotCore.cropAndEncode(image, 0, 0, size.width, size.height, 1, width, height) as Uint8Array;
return bytesToDataUrl(thumb, "image/png");
}

/**
* `source` is what the browser-frame tool stamps into the address bar. It is
* carried here rather than read in the editor because the editor runs in its
Expand Down Expand Up @@ -366,7 +389,7 @@ async function handleRequest(request: PopupRequest): Promise<PopupResponse> {
await setLastCaptureUi({ report, openedEditor });
await rememberInHistory(tab, report, images[0]!);
await bumpUsageCount();
return { ok: true, report, pngDataUrls: images.map((img) => bytesToDataUrl(img, "image/png")), openedEditor };
return { ok: true, report, previewDataUrl: await previewDataUrl(images[0]!), openedEditor };
}
case "captureVisible": {
const tab = await getActiveTab();
Expand All @@ -375,7 +398,7 @@ async function handleRequest(request: PopupRequest): Promise<PopupResponse> {
await setLastCaptureUi({ report, openedEditor: true });
await rememberInHistory(tab, report, images[0]!);
await bumpUsageCount();
return { ok: true, report, pngDataUrls: images.map((img) => bytesToDataUrl(img, "image/png")), openedEditor: true };
return { ok: true, report, previewDataUrl: await previewDataUrl(images[0]!), openedEditor: true };
}
case "captureSelectedArea": {
// The popup closes as soon as the user clicks into the page to start
Expand All @@ -398,7 +421,7 @@ async function handleRequest(request: PopupRequest): Promise<PopupResponse> {
return {
ok: true,
report: outcome.report,
pngDataUrls: outcome.images.map((img) => bytesToDataUrl(img, "image/png")),
previewDataUrl: await previewDataUrl(outcome.images[0]!),
openedEditor: true,
};
}
Expand Down
4 changes: 2 additions & 2 deletions apps/extension/src/popup/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,9 +575,9 @@ async function showCaptureResult(response: PopupResponse): Promise<void> {
if ("report" in response) {
reportEl.style.display = "block";
reportEl.textContent = JSON.stringify(response.report, null, 2);
if (response.pngDataUrls[0]) {
if (response.previewDataUrl) {
previewEl.style.display = "block";
previewEl.src = response.pngDataUrls[0];
previewEl.src = response.previewDataUrl;
}
// background/index.ts already persisted this (see chrome/last-capture-ui.ts)
// by the time this response reaches here, if it reaches here at all —
Expand Down
6 changes: 5 additions & 1 deletion apps/extension/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ export type PopupRequest =
export interface CaptureResult {
ok: true;
report: CaptureReport;
pngDataUrls: string[];
// Small downscaled PNG of the capture's first output image, for the popup's
// preview thumbnail. The full-size bytes stay in blob-store: putting them in
// this response crossed chrome.runtime.sendMessage's ~64MiB message cap and
// failed captures that had already succeeded.
previewDataUrl?: string;
// True when the capture was routed to the editor for crop/annotate/format
// review. False when it was too long for that (see capture-size.ts's
// needsFormatChoice), in which case nothing has been written or opened at
Expand Down