mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-04 00:28:33 +00:00
I used [pdfmake](https://www.npmjs.com/package/pdfmake) to implement an "export as PDF" feature, and I am happy to share with you! This should fix #13577, fix #8846, and fix #13959. A showcase: [Getting Started.pdf](https://github.com/user-attachments/files/24013057/Getting.Started.pdf) Although it might miss rendering some properties currently, it can evolve in the long run and provide a more native experience for the users. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** - Experimental "Export to PDF" option added to the export menu (behind a feature flag) - PDF export supports headings, paragraphs, lists, code blocks, tables, images, callouts, linked documents and embedded content * **Chores** - Added PDF rendering library and consolidated PDF utilities - Feature flag introduced to control rollout * **Tests** - Comprehensive unit tests added for PDF content rendering logic <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: DarkSky <darksky2048@gmail.com>
137 lines
3.6 KiB
TypeScript
137 lines
3.6 KiB
TypeScript
import { type Page } from '@playwright/test';
|
|
import { AsyncLock } from '@toeverything/infra/utils';
|
|
|
|
const IS_MAC = process.platform === 'darwin';
|
|
|
|
async function keyDownCtrlOrMeta(page: Page) {
|
|
if (IS_MAC) {
|
|
await page.keyboard.down('Meta');
|
|
} else {
|
|
await page.keyboard.down('Control');
|
|
}
|
|
}
|
|
|
|
async function keyUpCtrlOrMeta(page: Page) {
|
|
if (IS_MAC) {
|
|
await page.keyboard.up('Meta');
|
|
} else {
|
|
await page.keyboard.up('Control');
|
|
}
|
|
}
|
|
|
|
// It's not good enough, but better than calling keyDownCtrlOrMeta and keyUpCtrlOrMeta separately
|
|
export const withCtrlOrMeta = async (page: Page, fn: () => Promise<void>) => {
|
|
await keyDownCtrlOrMeta(page);
|
|
await fn();
|
|
await keyUpCtrlOrMeta(page);
|
|
};
|
|
|
|
export async function pressEnter(page: Page, count = 1) {
|
|
// avoid flaky test by simulate real user input
|
|
for (let i = 0; i < count; i++) {
|
|
await page.keyboard.press('Enter', { delay: 50 });
|
|
}
|
|
}
|
|
|
|
export async function pressArrowUp(page: Page, count = 1) {
|
|
for (let i = 0; i < count; i++) {
|
|
await page.keyboard.press('ArrowUp', { delay: 50 });
|
|
}
|
|
}
|
|
|
|
export async function pressArrowDown(page: Page, count = 1) {
|
|
for (let i = 0; i < count; i++) {
|
|
await page.keyboard.press('ArrowDown', { delay: 20 });
|
|
}
|
|
}
|
|
|
|
export async function pressTab(page: Page) {
|
|
await page.keyboard.press('Tab', { delay: 50 });
|
|
}
|
|
|
|
export async function pressShiftTab(page: Page) {
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('Tab', { delay: 50 });
|
|
await page.keyboard.up('Shift');
|
|
}
|
|
|
|
export async function pressShiftEnter(page: Page) {
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('Enter', { delay: 50 });
|
|
await page.keyboard.up('Shift');
|
|
}
|
|
|
|
export async function pressBackspace(page: Page, count = 1) {
|
|
for (let i = 0; i < count; i++) {
|
|
await page.keyboard.press('Backspace', { delay: 50 });
|
|
}
|
|
}
|
|
|
|
export async function pressEscape(page: Page, count = 1) {
|
|
for (let i = 0; i < count; i++) {
|
|
await page.keyboard.press('Escape', { delay: 50 });
|
|
}
|
|
}
|
|
|
|
export async function copyByKeyboard(page: Page) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('c', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
|
|
export async function cutByKeyboard(page: Page) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('x', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
|
|
export async function pasteByKeyboard(page: Page) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('v', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
|
|
export async function selectAllByKeyboard(page: Page) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('a', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
|
|
export async function undoByKeyboard(page: Page) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('z', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
|
|
const clipboardMutex = new AsyncLock();
|
|
|
|
export async function writeTextToClipboard(
|
|
page: Page,
|
|
text: string,
|
|
paste = true
|
|
) {
|
|
using _release = await clipboardMutex.acquire();
|
|
// paste the url
|
|
await page.evaluate(
|
|
async ([text]) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
navigator.clipboard.writeText('');
|
|
const e = new ClipboardEvent('paste', {
|
|
clipboardData: new DataTransfer(),
|
|
});
|
|
Object.defineProperty(e, 'target', {
|
|
writable: false,
|
|
value: document,
|
|
});
|
|
e.clipboardData!.setData('text/plain', text);
|
|
document.dispatchEvent(e);
|
|
},
|
|
[text]
|
|
);
|
|
if (paste) {
|
|
await keyDownCtrlOrMeta(page);
|
|
await page.keyboard.press('v', { delay: 50 });
|
|
await keyUpCtrlOrMeta(page);
|
|
}
|
|
}
|