@cyber-dash-tech/revela 0.4.1 → 0.4.3

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.
Files changed (2) hide show
  1. package/lib/pptx/export.ts +35 -22
  2. package/package.json +1 -1
@@ -17,13 +17,15 @@ import puppeteer, { type Browser, type Page } from "puppeteer-core"
17
17
  import { DOMParser, XMLSerializer } from "@xmldom/xmldom"
18
18
  import { unzipSync, zipSync, strFromU8, strToU8 } from "fflate"
19
19
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs"
20
+ import { createRequire } from "module"
20
21
  import { basename, dirname, extname, join, posix as pathPosix, resolve } from "path"
21
22
  import { randomBytes } from "crypto"
22
- import { fileURLToPath, pathToFileURL } from "url"
23
+ import { pathToFileURL } from "url"
23
24
 
24
25
  const CANVAS_W = 1920
25
26
  const CANVAS_H = 1080
26
27
  const PPT_REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
28
+ const requireFromExportModule = createRequire(import.meta.url)
27
29
 
28
30
  const CHROME_PATHS = [
29
31
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
@@ -151,11 +153,35 @@ export function derivePptxPath(htmlFilePath: string): string {
151
153
  return join(dir, `${name}.pptx`)
152
154
  }
153
155
 
156
+ export function resolveDomToPptxBundlePath(): string {
157
+ const entryPath = requireFromExportModule.resolve("dom-to-pptx")
158
+ const bundlePath = join(dirname(entryPath), "dom-to-pptx.bundle.js")
159
+
160
+ if (!existsSync(bundlePath)) {
161
+ throw new Error(`dom-to-pptx browser bundle not found: ${bundlePath}`)
162
+ }
163
+
164
+ return bundlePath
165
+ }
166
+
154
167
  function isLocalImageRef(ref: string): boolean {
155
168
  const pathPart = ref.split(/[?#]/)[0]
156
169
  return IMAGE_EXTS.has(extname(pathPart).toLowerCase())
157
170
  }
158
171
 
172
+ export function extractImageAssetRefsForPptx(htmlContent: string): string[] {
173
+ const assetRefPattern = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))|url\(\s*(?:"([^"]*)"|'([^']*)'|([^\s)]+))\s*\)/g
174
+ const refs = new Set<string>()
175
+ let match: RegExpExecArray | null
176
+
177
+ while ((match = assetRefPattern.exec(htmlContent)) !== null) {
178
+ const ref = match.slice(1).find((value): value is string => value !== undefined)
179
+ if (ref) refs.add(ref.trim())
180
+ }
181
+
182
+ return Array.from(refs)
183
+ }
184
+
159
185
  async function toDataUrlFromRef(ref: string, baseDir: string): Promise<string | null> {
160
186
  if (!ref || ref.startsWith("data:") || ref.startsWith("blob:") || ref.startsWith("#")) {
161
187
  return null
@@ -182,21 +208,15 @@ async function toDataUrlFromRef(ref: string, baseDir: string): Promise<string |
182
208
  }
183
209
  }
184
210
 
185
- async function inlineImageAssets(htmlContent: string, htmlFilePath: string): Promise<string> {
211
+ export async function inlineImageAssets(htmlContent: string, htmlFilePath: string): Promise<string> {
186
212
  const baseDir = dirname(resolve(htmlFilePath))
187
- const urlPattern = /(?:src=["']|url\(["']?)([^"')>\s]+)/g
188
- const refs = new Set<string>()
189
- let match: RegExpExecArray | null
190
-
191
- while ((match = urlPattern.exec(htmlContent)) !== null) {
192
- refs.add(match[1])
193
- }
213
+ const refs = extractImageAssetRefsForPptx(htmlContent)
194
214
 
195
- if (refs.size === 0) return htmlContent
215
+ if (refs.length === 0) return htmlContent
196
216
 
197
217
  const replacements = new Map<string, string>()
198
218
  await Promise.allSettled(
199
- Array.from(refs).map(async (ref) => {
219
+ refs.map(async (ref) => {
200
220
  const dataUrl = await toDataUrlFromRef(ref, baseDir)
201
221
  if (dataUrl) replacements.set(ref, dataUrl)
202
222
  })
@@ -211,13 +231,9 @@ async function inlineImageAssets(htmlContent: string, htmlFilePath: string): Pro
211
231
  }
212
232
 
213
233
  async function localizeExternalImages(htmlContent: string, tmpDir: string): Promise<LocalizeExternalImagesResult> {
214
- const urlPattern = /(?:src=["']|url\(["']?)(https?:\/\/[^"')>\s]+)/g
215
- const uniqueUrls = new Set<string>()
216
- let match: RegExpExecArray | null
217
-
218
- while ((match = urlPattern.exec(htmlContent)) !== null) {
219
- uniqueUrls.add(match[1])
220
- }
234
+ const uniqueUrls = new Set(
235
+ extractImageAssetRefsForPptx(htmlContent).filter((ref) => ref.startsWith("http://") || ref.startsWith("https://"))
236
+ )
221
237
 
222
238
  if (uniqueUrls.size === 0) {
223
239
  return {
@@ -831,10 +847,7 @@ export async function exportToPptx(
831
847
  ): Promise<ExportPptxResult> {
832
848
  const startMs = Date.now()
833
849
  const abs = resolve(htmlFilePath)
834
- const domToPptxBundlePath = resolve(
835
- dirname(fileURLToPath(import.meta.url)),
836
- "../../node_modules/dom-to-pptx/dist/dom-to-pptx.bundle.js"
837
- )
850
+ const domToPptxBundlePath = resolveDomToPptxBundlePath()
838
851
 
839
852
  if (!existsSync(abs)) {
840
853
  throw new Error(`File not found: ${abs}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-dash-tech/revela",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "OpenCode plugin that turns AI into an HTML slide deck generator",
5
5
  "type": "module",
6
6
  "main": "./index.ts",