@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/README.md +14 -14
- package/README.zh-CN.md +14 -14
- package/designs/lucent/DESIGN.md +2 -1
- package/designs/lucent/design.css +16 -6
- package/designs/lucent-dark/DESIGN.md +2 -1
- package/designs/lucent-dark/design.css +27 -12
- package/designs/monet/DESIGN.md +2 -1
- package/designs/monet/design.css +18 -8
- package/designs/starter/DESIGN.md +2 -1
- package/designs/starter/design.css +16 -6
- package/designs/summit/DESIGN.md +2 -1
- package/designs/summit/design.css +18 -8
- package/lib/browser/chrome.ts +92 -8
- package/lib/page-templates/built-in-preview.html +42 -30
- package/lib/page-templates/render.ts +51 -10
- package/lib/page-templates/templates/index.ts +1 -0
- package/lib/page-templates/templates/table.ts +3 -0
- package/lib/page-templates/vocabulary.ts +8 -2
- package/lib/pdf/export.ts +57 -31
- package/package.json +1 -1
- package/plugins/revela/.codex-plugin/plugin.json +1 -1
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
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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 (
|
|
440
|
-
const buf = await
|
|
441
|
-
screenshots.push(buf as
|
|
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
|
|
471
|
+
screenshots.push(Buffer.from(buf as Uint8Array))
|
|
446
472
|
}
|
|
447
473
|
}
|
|
448
474
|
} finally {
|
package/package.json
CHANGED