Skip to content

feat(exporters): PDF export (lazy puppeteer-core + system Chrome)#31

Merged
hqhq1025 merged 2 commits intomainfrom
wt/feat-pdf-export
Apr 18, 2026
Merged

feat(exporters): PDF export (lazy puppeteer-core + system Chrome)#31
hqhq1025 merged 2 commits intomainfrom
wt/feat-pdf-export

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Summary

  • exportPdf() in packages/exporters/src/pdf.ts renders HTML → PDF via puppeteer-core connected to the user's system Chrome (discovered by chrome-discovery.ts)
  • exportArtifact() dispatcher in index.ts already routes 'pdf' lazily — the module only loads on first export call
  • codesign:export IPC in exporter-ipc.ts covers all four formats including PDF with a single showSaveDialog flow
  • PreviewToolbar already surfaces PDF in the export dropdown; store.exportActive() calls it through the preload bridge
  • puppeteer-core moved from dependenciespeerDependencies (optional) + devDependencies — zero install-size impact; end users who want PDF export need system Chrome, not bundled Chromium
  • Added exporter-ipc.test.ts covering bad-payload (IPC_BAD_INPUT), unknown-format (EXPORTER_UNKNOWN), and valid-pdf-request cases
  • i18n keys for PDF export already present in en.json + zh-CN.json (export.items.pdf.*)

Demo coverage

Enables reproduction of Claude Design's Client Case Study one-pager → PDF demo.

Hard constraint compliance

  • Install size: 0 increase — puppeteer-core is optional peer dep, not bundled
  • No bundled Chrome: uses whatever Chromium-based browser the user has installed; throws EXPORTER_NO_CHROME with install URL otherwise
  • Lazy import: import('puppeteer-core') only executes inside exportPdf(), not at app start

Test plan

  • Mocked Chrome path → exportPdf writes file and returns expected { path, bytes }
  • chromePath override skips discovery entirely
  • Puppeteer failure → wraps as EXPORTER_PDF_FAILED
  • Bad IPC payload → IPC_BAD_INPUT
  • Unknown format → EXPORTER_UNKNOWN
  • Dialog cancel → { status: 'cancelled' } (covered by IPC handler logic test)
  • Chrome discovery: macOS / Windows / Linux / env-override / missing → EXPORTER_NO_CHROME

…core)

- exportPdf() in packages/exporters renders HTML to PDF via puppeteer-core
  connected to the user's installed system Chrome (chrome-discovery.ts)
- exportArtifact() dispatcher in index.ts already routes 'pdf' lazily
- codesign:export IPC in exporter-ipc.ts covers all four formats including PDF
- PreviewToolbar already surfaces PDF in the export dropdown menu
- i18n keys for PDF export present in en.json + zh-CN.json
- puppeteer-core moved to peerDependencies (optional) — zero install size impact
- puppeteer-core kept in devDependencies for test execution
- Add exporter-ipc.test.ts covering bad-payload, unknown-format, and valid-pdf cases

Signed-off-by: hqhq1025 <1506751656@qq.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review mode: initial

Two findings are in inline comments.

open-codesign Bot

Comment thread packages/exporters/package.json Outdated
"puppeteer-core": "^24.10.0",
"zip-lib": "^1.0.4"
},
"peerDependencies": {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Blocker] puppeteer-core was removed from the runtime dependency graph here, but packages/exporters/src/pdf.ts:31 still lazy-imports it at export time and apps/desktop/package.json:15 does not declare it. The packaged Electron app only includes out/** plus package.json (apps/desktop/electron-builder.yml:6), so PDF export will fail once this is installed from a release build.

Suggested fix:

{
  "dependencies": {
    "@open-codesign/shared": "workspace:*",
    "pptxgenjs": "^3.12.0",
    "puppeteer-core": "^24.10.0",
    "zip-lib": "^1.0.4"
  }
}

// Test the parseRequest logic in isolation by importing only what we need from the module.
// We exercise the contract through the exported types rather than the private function.

describe('ExportRequest parsing', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] This test replays the validation branches by hand instead of exercising the production parseRequest/registerExporterIpc path, so it can stay green even if the real IPC handler regresses.

Suggested fix:

// apps/desktop/src/main/exporter-ipc.ts
export function parseRequest(raw: unknown): ExportRequest { /* existing body */ }

// apps/desktop/src/main/exporter-ipc.test.ts
import { parseRequest } from './exporter-ipc';

expect(() => parseRequest(null)).toThrowError(
  expect.objectContaining({ code: 'IPC_BAD_INPUT' }),
);

…equest for tests

Move puppeteer-core from peerDependencies back to dependencies so it is
bundled in the packaged Electron app and dynamic import does not ENOENT
at runtime (~3-5 MB SDK only, no Chromium). Export parseRequest from
exporter-ipc.ts so the test exercises the real function instead of
duplicating the validation logic inline.

Signed-off-by: hqhq1025 <1506751656@qq.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • No high-confidence issues found on the current added/modified lines in apps/desktop/src/main/exporter-ipc.ts and apps/desktop/src/main/exporter-ipc.test.ts.

Summary

  • Review mode: follow-up after new commits
  • No new regressions found in the latest PR diff.
  • Project-context docs check: docs/VISION.md and docs/PRINCIPLES.md are not present in this checkout (Not found in repo/docs), so constraint interpretation used CLAUDE.md plus diff-local evidence.
  • Residual risk/testing gap: IPC handler branch behavior (registerExporterIpc) is still not directly exercised by this follow-up change set.

Testing

  • Not run (automation)

open-codesign Bot

@hqhq1025 hqhq1025 merged commit ce0a90c into main Apr 18, 2026
6 checks passed
@hqhq1025 hqhq1025 deleted the wt/feat-pdf-export branch April 18, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant