@cyber-dash-tech/revela 0.19.2 → 0.19.4

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/pdf/export.ts CHANGED
@@ -187,6 +187,54 @@ async function toDataUrlFromRef(ref: string, baseDir: string): Promise<string |
187
187
  }
188
188
  }
189
189
 
190
+ function withExportBaseHref(html: string, htmlFilePath: string): string {
191
+ const baseHref = pathToFileURL(`${dirname(resolve(htmlFilePath))}/`).href
192
+ const baseTag = `<base href="${baseHref}">`
193
+ if (/<base\b/i.test(html)) return html
194
+ if (/<head[^>]*>/i.test(html)) {
195
+ return html.replace(/<head([^>]*)>/i, `<head$1>\n${baseTag}`)
196
+ }
197
+ return `${baseTag}\n${html}`
198
+ }
199
+
200
+ async function prepareSlidesForExport(page: any): Promise<void> {
201
+ await page.evaluate((canvasWidth: number, canvasHeight: number) => {
202
+ document.documentElement.style.scrollSnapType = "none"
203
+ document.documentElement.style.overflow = "visible"
204
+ document.body.style.overflow = "visible"
205
+ document.body.style.margin = "0"
206
+
207
+ const style = document.createElement("style")
208
+ style.setAttribute("data-revela-export-style", "true")
209
+ style.textContent = `
210
+ html, body { scroll-snap-type: none !important; overflow: visible !important; }
211
+ .slide {
212
+ width: ${canvasWidth}px !important;
213
+ min-width: ${canvasWidth}px !important;
214
+ height: ${canvasHeight}px !important;
215
+ min-height: ${canvasHeight}px !important;
216
+ display: flex !important;
217
+ align-items: center !important;
218
+ justify-content: center !important;
219
+ overflow: hidden !important;
220
+ scroll-snap-align: none !important;
221
+ }
222
+ .slide-canvas {
223
+ width: ${canvasWidth}px !important;
224
+ height: ${canvasHeight}px !important;
225
+ transform: none !important;
226
+ transform-origin: center center !important;
227
+ }
228
+ `
229
+ document.head.appendChild(style)
230
+
231
+ document.querySelectorAll<HTMLElement>(".slide-canvas").forEach((canvas) => {
232
+ canvas.style.transform = "none"
233
+ canvas.style.transformOrigin = "center center"
234
+ })
235
+ }, CANVAS_W, CANVAS_H)
236
+ }
237
+
190
238
  export async function inlineImageAssetsForPdf(htmlContent: string, htmlFilePath: string): Promise<string> {
191
239
  const baseDir = dirname(resolve(htmlFilePath))
192
240
  const refs = extractImageAssetRefsForPdf(htmlContent)
@@ -358,7 +406,7 @@ async function screenshotDeckSlides(htmlFilePath: string, label: "pdf" | "png"):
358
406
  try {
359
407
  const originalHtml = readFileSync(abs, "utf-8")
360
408
  const localizedHtml = await localizeExternalImages(originalHtml, tmpDir)
361
- const patchedHtml = await inlineImageAssetsForPdf(localizedHtml, abs)
409
+ const patchedHtml = withExportBaseHref(await inlineImageAssetsForPdf(localizedHtml, abs), abs)
362
410
  tmpHtmlPath = join(tmpDir, "index.html")
363
411
  writeFileSync(tmpHtmlPath, patchedHtml, "utf-8")
364
412
  } catch (err) {
@@ -386,13 +434,7 @@ async function screenshotDeckSlides(htmlFilePath: string, label: "pdf" | "png"):
386
434
  // Wait for fonts (Google Fonts may still be external) and CSS animations to settle
387
435
  await new Promise((r) => setTimeout(r, 2000))
388
436
 
389
- // Disable scroll-snap so offsetParent-based clip coords are accurate.
390
- // Also ensure html/body are tall enough to contain all slides without clipping.
391
- await page.evaluate(() => {
392
- document.documentElement.style.scrollSnapType = "none"
393
- document.documentElement.style.overflow = "visible"
394
- document.body.style.overflow = "visible"
395
- })
437
+ await prepareSlidesForExport(page)
396
438
 
397
439
  const slideCount: number = await page.evaluate(
398
440
  () => document.querySelectorAll(".slide").length
@@ -417,32 +459,16 @@ async function screenshotDeckSlides(htmlFilePath: string, label: "pdf" | "png"):
417
459
  // Wait for CSS transitions and JS rendering (ECharts animations, etc.)
418
460
  await new Promise((r) => setTimeout(r, 800))
419
461
 
420
- // Compute screenshot target absolute position by walking the offsetParent chain.
421
- // getBoundingClientRect() returns viewport-relative coords (always near 0,0)
422
- // unusable as screenshot clip coordinates without adding scrollY.
423
- // offsetParent walk gives document-absolute coords that Puppeteer clip expects.
424
- const clipRect = await page.evaluate((i: number) => {
425
- const slide = document.querySelectorAll(".slide")[i] as HTMLElement | null
426
- if (!slide) return null
427
- const target = (slide.querySelector(".slide-canvas") as HTMLElement | null) ?? slide
428
- let top = 0
429
- let left = 0
430
- let el: HTMLElement | null = target
431
- while (el) {
432
- top += el.offsetTop
433
- left += el.offsetLeft
434
- el = el.offsetParent as HTMLElement | null
435
- }
436
- return { x: left, y: top, width: target.offsetWidth, height: target.offsetHeight }
437
- }, idx)
462
+ const target = await page.$(`.slide:nth-of-type(${idx + 1}) > .slide-canvas`)
463
+ ?? await page.$(`.slide:nth-of-type(${idx + 1})`)
464
+ const box = target ? await target.boundingBox() : null
438
465
 
439
- if (clipRect && clipRect.width > 0 && clipRect.height > 0) {
440
- const buf = await page.screenshot({ type: "png", clip: clipRect })
441
- screenshots.push(buf as Buffer)
466
+ if (target && box && box.width > 0 && box.height > 0) {
467
+ const buf = await target.screenshot({ type: "png" })
468
+ screenshots.push(Buffer.from(buf as Uint8Array))
442
469
  } else {
443
- // Fallback: screenshot full viewport
444
470
  const buf = await page.screenshot({ type: "png" })
445
- screenshots.push(buf as Buffer)
471
+ screenshots.push(Buffer.from(buf as Uint8Array))
446
472
  }
447
473
  }
448
474
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-dash-tech/revela",
3
- "version": "0.19.2",
3
+ "version": "0.19.4",
4
4
  "description": "Codex-first CLI/MCP workspace for trusted narrative artifacts from local sources, research, and evidence",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revela",
3
- "version": "0.19.2",
3
+ "version": "0.19.4",
4
4
  "description": "Use Revela in Codex to specify, research, plan, make, and export trusted narrative decision artifacts.",
5
5
  "author": {
6
6
  "name": "cyber-dash-tech",