@cyber-dash-tech/revela 0.17.16 → 0.17.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/html-export/deck-detect.ts +58 -32
- package/lib/pdf/export.ts +5 -6
- package/package.json +1 -1
- package/plugins/revela/.mcp.json +1 -1
|
@@ -22,55 +22,81 @@ export async function detectDeckHtmlWithBrowser(browser: Browser, htmlFilePath:
|
|
|
22
22
|
try {
|
|
23
23
|
await page.goto(pathToFileURL(htmlFilePath).href, { waitUntil: "domcontentloaded", timeout: 15000 })
|
|
24
24
|
return await page.evaluate(() => {
|
|
25
|
+
const CANVAS_WIDTH = 1920
|
|
26
|
+
const CANVAS_HEIGHT = 1080
|
|
27
|
+
const DIMENSION_TOLERANCE = 2
|
|
28
|
+
const isDeckCanvasSize = (el: HTMLElement): boolean => {
|
|
29
|
+
const rect = el.getBoundingClientRect()
|
|
30
|
+
return (
|
|
31
|
+
Math.abs(rect.width - CANVAS_WIDTH) <= DIMENSION_TOLERANCE &&
|
|
32
|
+
Math.abs(rect.height - CANVAS_HEIGHT) <= DIMENSION_TOLERANCE
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
const slides = Array.from(document.querySelectorAll(".slide")) as HTMLElement[]
|
|
26
37
|
if (slides.length === 0) {
|
|
27
38
|
return { isDeck: false, slideCount: 0, reason: "no .slide elements found" }
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
const indexValues = slides.map((slide) => slide.getAttribute("data-slide-index"))
|
|
42
|
+
const seen = new Set<number>()
|
|
43
|
+
for (let i = 0; i < indexValues.length; i++) {
|
|
44
|
+
const raw = indexValues[i]
|
|
45
|
+
if (raw === null || raw.trim() === "") {
|
|
46
|
+
return {
|
|
47
|
+
isDeck: false,
|
|
48
|
+
slideCount: slides.length,
|
|
49
|
+
reason: `slide ${i + 1} is missing data-slide-index`,
|
|
50
|
+
}
|
|
36
51
|
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const indexValues = slides
|
|
40
|
-
.map((slide) => slide.getAttribute("data-slide-index"))
|
|
41
|
-
.filter((value): value is string => value !== null && value.trim() !== "")
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
if (
|
|
53
|
+
const parsed = Number(raw)
|
|
54
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
45
55
|
return {
|
|
46
56
|
isDeck: false,
|
|
47
57
|
slideCount: slides.length,
|
|
48
|
-
reason:
|
|
58
|
+
reason: `slide ${i + 1} has invalid data-slide-index "${raw}"`,
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
isDeck: false,
|
|
58
|
-
slideCount: slides.length,
|
|
59
|
-
reason: `slide ${i + 1} has invalid data-slide-index "${indexValues[i]}"`,
|
|
60
|
-
}
|
|
61
|
+
if (seen.has(parsed)) {
|
|
62
|
+
return {
|
|
63
|
+
isDeck: false,
|
|
64
|
+
slideCount: slides.length,
|
|
65
|
+
reason: `duplicate data-slide-index "${parsed}"`,
|
|
61
66
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
67
|
+
}
|
|
68
|
+
if (parsed !== i + 1) {
|
|
69
|
+
return {
|
|
70
|
+
isDeck: false,
|
|
71
|
+
slideCount: slides.length,
|
|
72
|
+
reason: `slide ${i + 1} has data-slide-index "${parsed}", expected "${i + 1}"`,
|
|
68
73
|
}
|
|
69
|
-
seen.add(parsed)
|
|
70
74
|
}
|
|
75
|
+
seen.add(parsed)
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
let usedSlideAsCanvas = false
|
|
79
|
+
for (let i = 0; i < slides.length; i++) {
|
|
80
|
+
const slide = slides[i]
|
|
81
|
+
if (slide.querySelector(".slide-canvas")) continue
|
|
82
|
+
if (isDeckCanvasSize(slide)) {
|
|
83
|
+
usedSlideAsCanvas = true
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
isDeck: false,
|
|
88
|
+
slideCount: slides.length,
|
|
89
|
+
reason: `.slide ${i + 1} has no .slide-canvas and is not 1920x1080`,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
isDeck: true,
|
|
95
|
+
slideCount: slides.length,
|
|
96
|
+
reason: usedSlideAsCanvas
|
|
97
|
+
? "valid deck contract: slide-as-canvas"
|
|
98
|
+
: "valid deck contract: slide-canvas",
|
|
99
|
+
}
|
|
74
100
|
})
|
|
75
101
|
} finally {
|
|
76
102
|
await page.close().catch(() => undefined)
|
package/lib/pdf/export.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* rewrite the HTML to use file:// local paths — avoids CDN/CORS/headless issues
|
|
10
10
|
* 3. Navigate to the patched HTML file
|
|
11
11
|
* 4. For each .slide: force .reveal.visible, wait 800ms, screenshot .slide-canvas
|
|
12
|
-
* using offsetParent-chain absolute coordinates
|
|
12
|
+
* or slide-as-canvas fallback using offsetParent-chain absolute coordinates
|
|
13
13
|
* 5. Assemble screenshots into a multi-page PDF (16:9, 1920×1080pt per page) via pdf-lib
|
|
14
14
|
* 6. Write PDF alongside the HTML file (same directory, .html → .pdf)
|
|
15
15
|
* 7. Clean up temp dir
|
|
@@ -335,24 +335,23 @@ export async function exportDeckToPdf(htmlFilePath: string): Promise<Omit<Export
|
|
|
335
335
|
// Wait for CSS transitions and JS rendering (ECharts animations, etc.)
|
|
336
336
|
await new Promise((r) => setTimeout(r, 800))
|
|
337
337
|
|
|
338
|
-
// Compute
|
|
338
|
+
// Compute screenshot target absolute position by walking the offsetParent chain.
|
|
339
339
|
// getBoundingClientRect() returns viewport-relative coords (always near 0,0) —
|
|
340
340
|
// unusable as screenshot clip coordinates without adding scrollY.
|
|
341
341
|
// offsetParent walk gives document-absolute coords that Puppeteer clip expects.
|
|
342
342
|
const clipRect = await page.evaluate((i: number) => {
|
|
343
343
|
const slide = document.querySelectorAll(".slide")[i] as HTMLElement | null
|
|
344
344
|
if (!slide) return null
|
|
345
|
-
const
|
|
346
|
-
if (!canvas) return null
|
|
345
|
+
const target = (slide.querySelector(".slide-canvas") as HTMLElement | null) ?? slide
|
|
347
346
|
let top = 0
|
|
348
347
|
let left = 0
|
|
349
|
-
let el: HTMLElement | null =
|
|
348
|
+
let el: HTMLElement | null = target
|
|
350
349
|
while (el) {
|
|
351
350
|
top += el.offsetTop
|
|
352
351
|
left += el.offsetLeft
|
|
353
352
|
el = el.offsetParent as HTMLElement | null
|
|
354
353
|
}
|
|
355
|
-
return { x: left, y: top, width:
|
|
354
|
+
return { x: left, y: top, width: target.offsetWidth, height: target.offsetHeight }
|
|
356
355
|
}, idx)
|
|
357
356
|
|
|
358
357
|
if (clipRect && clipRect.width > 0 && clipRect.height > 0) {
|
package/package.json
CHANGED