@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.
@@ -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 missingCanvas = slides.findIndex((slide) => !slide.querySelector(".slide-canvas"))
31
- if (missingCanvas >= 0) {
32
- return {
33
- isDeck: false,
34
- slideCount: slides.length,
35
- reason: `.slide ${missingCanvas + 1} has no .slide-canvas`,
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
- if (indexValues.length > 0) {
44
- if (indexValues.length !== slides.length) {
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: "some slides have data-slide-index and some do not",
58
+ reason: `slide ${i + 1} has invalid data-slide-index "${raw}"`,
49
59
  }
50
60
  }
51
-
52
- const seen = new Set<number>()
53
- for (let i = 0; i < indexValues.length; i++) {
54
- const parsed = Number(indexValues[i])
55
- if (!Number.isInteger(parsed) || parsed < 1) {
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
- if (seen.has(parsed)) {
63
- return {
64
- isDeck: false,
65
- slideCount: slides.length,
66
- reason: `duplicate data-slide-index "${parsed}"`,
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
- return { isDeck: true, slideCount: slides.length, reason: "valid deck contract" }
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 .slide-canvas absolute position by walking the offsetParent chain.
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 canvas = slide.querySelector(".slide-canvas") as HTMLElement | null
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 = canvas
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: canvas.offsetWidth, height: canvas.offsetHeight }
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-dash-tech/revela",
3
- "version": "0.17.16",
3
+ "version": "0.17.17",
4
4
  "description": "OpenCode plugin for trusted narrative artifacts from local sources, research, and evidence",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "revela": {
4
4
  "command": "npx",
5
- "args": ["-y", "@cyber-dash-tech/revela@0.17.16", "mcp"]
5
+ "args": ["-y", "@cyber-dash-tech/revela@0.17.17", "mcp"]
6
6
  }
7
7
  }
8
8
  }