Skip to content

✨(frontend) Export presenter slides as PDF - #2487

Open
PanchoutNathan wants to merge 4 commits into
mainfrom
feature/present-mode-pdf-watermark
Open

✨(frontend) Export presenter slides as PDF#2487
PanchoutNathan wants to merge 4 commits into
mainfrom
feature/present-mode-pdf-watermark

Conversation

@PanchoutNathan

Copy link
Copy Markdown
Contributor

Purpose

  • Add a Docs logo watermark to presenter slides.
  • Add a "Download PDF" action to presenter controls.
  • Render presenter slides as landscape print pages.
  • Keep file blocks printable and clip tall slides to one page.

Refs #2466
Closes #2446
Closes #2472

@PanchoutNathan PanchoutNathan self-assigned this Jul 1, 2026
@PanchoutNathan
PanchoutNathan marked this pull request as draft July 1, 2026 14:34
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Size Change: +4.05 kB (+0.09%)

Total Size: 4.37 MB

📦 View Changed
Filename Size Change
apps/impress/out/_next/static/0b0c3ff3/_buildManifest.js 0 B -677 B (removed) 🏆
apps/impress/out/_next/static/bea5df39/_buildManifest.js 677 B +677 B (new file) 🆕
apps/impress/out/_next/static/chunks/3347.js 0 B -4 kB (removed) 🏆
apps/impress/out/_next/static/chunks/6326.js 8.03 kB +8.03 kB (new file) 🆕
apps/impress/out/_next/static/chunks/6803.js 0 B -24.1 kB (removed) 🏆
apps/impress/out/_next/static/chunks/7444.js 24.1 kB +24.1 kB (new file) 🆕

compressed-size-action

wrapMediaWithLink / wrapInterlinksWithAnchor defaulted to document.
Accept a root node so the presenter export can reuse them on its
off-screen tree.
Show a monochrome Docs logo in the slide corner (web and PDF) to signal
the document was made with Docs.

Closes #2472
Render the slides off-screen as A4 landscape pages and print them via
the browser, one slide per page with the watermark. Add a "Download PDF"
action to the floating bar.

Closes #2446
Exercise PDF print pages, file blocks, and tall slide clipping.

Keep coverage scoped to the presenter export workflow.
@PanchoutNathan
PanchoutNathan force-pushed the feature/present-mode-pdf-watermark branch from 5882aca to c1afe04 Compare August 5, 2026 06:53
@PanchoutNathan
PanchoutNathan requested a review from AntoLC August 5, 2026 07:47
@AntoLC
AntoLC marked this pull request as ready for review August 6, 2026 08:24
@AntoLC AntoLC added enhancement improve an existing feature frontend export labels Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Presenter mode now supports PDF export through a new Download PDF action. The export mounts all slides as A4 landscape print pages, adds Docs logo branding, converts media and interlinks for print output, waits for rendering, invokes browser printing, and removes print artifacts afterward. Tests cover component rendering, scoped print wrappers, generated PDF content, file links, tall slides, and cleanup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: antolc

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: exporting presenter slides as a PDF.
Description check ✅ Passed The description directly covers PDF export, watermarking, landscape pages, printable files, and slide clipping.
Linked Issues check ✅ Passed The changes implement PDF export for presenter mode and add the Docs logo watermark to web and PDF views as required by issues #2446 and #2472.
Out of Scope Changes check ✅ Passed The implementation and tests remain focused on presenter PDF export, watermarking, print layout, printable files, and content clipping.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/present-mode-pdf-watermark

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/apps/impress/src/features/docs/doc-presenter/utils_print.tsx`:
- Around line 137-145: Update printPresenterSlides so it returns a promise that
resolves only when cleanupOnce completes, including the afterprint and
fallback-timer paths. In the export flow, await this promise before clearing
isExportingPdf, keeping the export lock active through cleanup; add a test
covering window.print() returning before afterprint.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bff3960d-f603-45e9-80c4-7301a3fd23df

📥 Commits

Reviewing files that changed from the base of the PR and between b956be1 and c1afe04.

⛔ Files ignored due to path filters (1)
  • src/frontend/apps/impress/src/features/docs/doc-presenter/assets/logo-docs-mono.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • src/frontend/apps/e2e/__tests__/app-impress/presenter-mode.spec.ts
  • src/frontend/apps/impress/src/features/docs/doc-export/__tests__/utilsPrint.test.ts
  • src/frontend/apps/impress/src/features/docs/doc-export/utils_print.ts
  • src/frontend/apps/impress/src/features/docs/doc-presenter/__tests__/PresenterPrintDocument.spec.tsx
  • src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterDocsLogo.tsx
  • src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterFloatingBar.tsx
  • src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterOverlay.tsx
  • src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterPrintDocument.tsx
  • src/frontend/apps/impress/src/features/docs/doc-presenter/utils_print.tsx

Comment on lines +137 to +145
try {
window.addEventListener('afterprint', cleanupOnce, { once: true });
await waitForPresenterPrintReady(container);
scopedCleanups.push(wrapInterlinksWithAnchor(container));
scopedCleanups.push(wrapMediaWithLink(container));
await nextFrame();
window.print();
// Fallback in case `afterprint` never fires (some browsers/headless).
fallbackTimer = window.setTimeout(cleanupOnce, PRINT_CLEANUP_DELAY_MS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate file and relevant symbols"
fd -a 'utils_print.tsx' . || true
echo
rg -n "printPresenterSlides|PresenterOverlay|isExportingPdf|cleanupOnce|removeExistingPrintArtifacts|afterprint|afterprint|fallbackTimer|PRINT_CLEANUP_DELAY_MS" . -S

echo
file="$(fd 'utils_print.tsx' src/frontend/apps/impress/src/features/docs/doc-presenter | head -n 1 || true)"
if [ -n "$file" ]; then
  echo
  echo "File: $file"
  wc -l "$file"
  echo
  sed -n '1,240p' "$file" | cat -n
fi

echo
python3 - <<'PY'
from pathlib import Path
p = next(Path('.').rglob('utils_print.tsx'), None)
print('path=', p)
if p:
    s=p.read_text()
    for name in ['printPresenterSlides', 'cleanupOnce', 'cleanupAll', 'removeExistingPrintArtifacts', 'wrapInterlinksWithAnchor', 'wrapMediaWithLink', 'waitForPresenterPrintReady']:
        print(name, 'count=', s.count(name))
PY

Repository: suitenumerique/docs

Length of output: 10491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate file and relevant symbols"
fd -a 'utils_print.tsx' . || true
echo
rg -n "printPresenterSlides|PresenterOverlay|isExportingPdf|cleanupOnce|removeExistingPrintArtifacts|afterprint|fallbackTimer|PRINT_CLEANUP_DELAY_MS" . -S

echo
file="$(fd 'utils_print.tsx' src/frontend/apps/impress/src/features/docs/doc-presenter | head -n 1 || true)"
if [ -n "$file" ]; then
  echo
  echo "File: $file"
  wc -l "$file"
  echo
  sed -n '1,240p' "$file" | cat -n
fi

echo
python3 - <<'PY'
from pathlib import Path
p = next(Path('.').rglob('utils_print.tsx'), None)
print('path=', p)
if p:
    s=p.read_text()
    for name in ['printPresenterSlides', 'cleanupOnce', 'cleanupAll', 'removeExistingPrintArtifacts', 'wrapInterlinksWithAnchor', 'wrapMediaWithLink', 'waitForPresenterPrintReady']:
        print(name, 'count=', s.count(name))
PY

Repository: suitenumerique/docs

Length of output: 10491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd 'PresenterOverlay.tsx' src/frontend/apps/impress/src/features/docs/doc-presenter/components | head -n 1 || true)"
if [ -n "$file" ]; then
  echo "File: $file"
  wc -l "$file"
  echo
  sed -n '1,180p' "$file" | cat -n
fi

echo
python3 - <<'PY'
from pathlib import Path
p=next(Path('src/frontend/apps/impress/src/features/docs/doc-presenter').rglob('PresenterOverlay.tsx'), None)
text=p.read_text() if p else ''
for name in ['isExportingPdf', 'onExportPdf', 'printPresenterSlides', 'removeExistingPrintArtifacts']:
    print(name, text.count(name))
PY

Repository: suitenumerique/docs

Length of output: 7412


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd 'PresenterFloatingBar.tsx' src/frontend/apps/impress/src/features/docs/doc-presenter/components | head -n 1 || true)"
if [ -n "$file" ]; then
  echo "File: $file"
  wc -l "$file"
  echo
  sed -n '1,180p' "$file" | cat -n
fi

echo
p="${file:-}"
if [ -n "$p" ]; then
  text=$(cat "$p")
  for name in ['onExportPdf', 'isDisabled', 'isExportingPdf', 'exportPdf']; do
    count=$(printf '%s' "$text" | grep -F -c "$name" || true)
    echo "$name -> $count"
  done
fi

Repository: suitenumerique/docs

Length of output: 6908


Keep the export lock alive until print cleanup finishes.

printPresenterSlides resolves after window.print() returns, while afterprint or the fallback timer may still run. A second export can start once isExportingPdf clears and call mountPresenterPrintSlides(), which removes the previous export’s DOM/style artifacts. Return a promise from printPresenterSlides, resolve it in cleanupOnce, await it before clearing isExportingPdf, and add a test for window.print() returning before afterprint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/apps/impress/src/features/docs/doc-presenter/utils_print.tsx`
around lines 137 - 145, Update printPresenterSlides so it returns a promise that
resolves only when cleanupOnce completes, including the afterprint and
fallback-timer paths. In the export flow, await this promise before clearing
isExportingPdf, keeping the export lock active through cleanup; add a test
covering window.print() returning before afterprint.

@AntoLC AntoLC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On the code side, looks good.
I got some problems with the pdf export though, here a video:

Enregistrement.2026-08-06.105119.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement improve an existing feature export frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add docs logo watermark in corner Add PDF export of presenter mode

2 participants