Skip to content

Commit 1534125

Browse files
committed
debug(pdf-server): add __pdfDebug() helper + log skipped baseline annotations
Adds window.__pdfDebug() that dumps annotationMap, baseline, layer children, and localStorage diff to diagnose ghost annotations (visible on canvas but not in panel, not selectable). Also logs when importPdfjsAnnotation returns null for non-widget annotations, and surfaces any thrown errors during baseline import instead of silently swallowing them.
1 parent 634b7f9 commit 1534125

1 file changed

Lines changed: 75 additions & 2 deletions

File tree

examples/pdf-server/src/mcp-app.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,10 +3407,27 @@ async function loadBaselineAnnotations(
34073407
if (!annotationMap.has(def.id)) {
34083408
annotationMap.set(def.id, { def, elements: [] });
34093409
}
3410+
} else if (ann.annotationType !== 20) {
3411+
// Widget (type 20) is expected to be skipped; anything else we
3412+
// don't import will still be painted by page.render() onto the
3413+
// canvas as unselectable pixels. Log so we can diagnose
3414+
// "ghost annotations" (visible but not in panel, not clickable).
3415+
log.info(
3416+
`[WARN] Baseline: skipped PDF annotation on page ${pageNum}`,
3417+
`type=${ann.annotationType}`,
3418+
`subtype=${ann.subtype ?? "?"}`,
3419+
`name=${ann.name ?? "?"}`,
3420+
`rect=${ann.rect ? JSON.stringify(ann.rect) : "none"}`,
3421+
);
34103422
}
34113423
}
3412-
} catch {
3413-
// Skip pages that fail to load annotations
3424+
} catch (err) {
3425+
// Log the error — a thrown import for one annotation silently
3426+
// drops the REST of that page's annotations too.
3427+
log.info(
3428+
`[WARN] Baseline: page ${pageNum} annotation import failed:`,
3429+
err,
3430+
);
34143431
}
34153432
}
34163433
log.info(
@@ -5376,6 +5393,62 @@ app.connect().then(() => {
53765393
updateAnnotationsBadge();
53775394
});
53785395

5396+
// Debug helper: dump all annotation state. Run in DevTools console as
5397+
// `__pdfDebug()` to diagnose ghost annotations (visible on canvas but not
5398+
// in panel / not selectable). Returns a copy-pasteable JSON object.
5399+
(window as unknown as { __pdfDebug: () => unknown }).__pdfDebug = () => {
5400+
const out = {
5401+
annotationMap: [...annotationMap.entries()].map(([id, t]) => ({
5402+
id,
5403+
type: t.def.type,
5404+
page: t.def.page,
5405+
hasElements: t.elements.length,
5406+
// Trim imageData — can be megabytes of base64
5407+
def:
5408+
t.def.type === "image"
5409+
? { ...t.def, imageData: t.def.imageData ? "<omitted>" : undefined }
5410+
: t.def,
5411+
})),
5412+
pdfBaselineAnnotations: pdfBaselineAnnotations.map((d) => ({
5413+
id: d.id,
5414+
type: d.type,
5415+
page: d.page,
5416+
})),
5417+
annotationLayerChildren: [...annotationLayerEl.children].map((el) => ({
5418+
tag: el.tagName,
5419+
class: el.className,
5420+
})),
5421+
formLayerChildren: [...formLayerEl.children].map((el) => ({
5422+
tag: el.tagName,
5423+
class: el.className,
5424+
})),
5425+
localStorageKey: annotationStorageKey(),
5426+
localStorageRaw: (() => {
5427+
const k = annotationStorageKey();
5428+
if (!k) return null;
5429+
const raw = localStorage.getItem(k);
5430+
if (!raw) return null;
5431+
// Parse and trim imageData
5432+
try {
5433+
const d = JSON.parse(raw);
5434+
if (Array.isArray(d.added)) {
5435+
d.added = d.added.map((a: { imageData?: string }) =>
5436+
a.imageData ? { ...a, imageData: "<omitted>" } : a,
5437+
);
5438+
}
5439+
return d;
5440+
} catch {
5441+
return { parseError: true, length: raw.length };
5442+
}
5443+
})(),
5444+
currentPage,
5445+
isDirty,
5446+
panelOpen: annotationPanelOpen,
5447+
};
5448+
console.log(JSON.stringify(out, null, 2));
5449+
return out;
5450+
};
5451+
53795452
// =============================================================================
53805453
// Image from File (shared by drag-drop and paste)
53815454
// =============================================================================

0 commit comments

Comments
 (0)