Compare commits

..

1 Commits

Author SHA1 Message Date
DarkSky
52f4461fff chore: bump deps 2026-03-11 15:50:07 +08:00
11 changed files with 1533 additions and 1963 deletions

View File

@@ -17,14 +17,7 @@ export async function printToPdf(
return new Promise<void>((resolve, reject) => {
const iframe = document.createElement('iframe');
document.body.append(iframe);
// Use a hidden but rendering-enabled state instead of display: none
Object.assign(iframe.style, {
visibility: 'hidden',
position: 'absolute',
width: '0',
height: '0',
border: 'none',
});
iframe.style.display = 'none';
iframe.srcdoc = '<!DOCTYPE html>';
iframe.onload = async () => {
if (!iframe.contentWindow) {
@@ -35,44 +28,6 @@ export async function printToPdf(
reject(new Error('Root element not defined, unable to print pdf'));
return;
}
const doc = iframe.contentWindow.document;
doc.write(`<!DOCTYPE html><html><head><style>@media print {
html, body {
height: initial !important;
overflow: initial !important;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
color: #000 !important;
background: #fff !important;
color-scheme: light !important;
}
::-webkit-scrollbar {
display: none;
}
:root, body {
--affine-text-primary: #000 !important;
--affine-text-secondary: #111 !important;
--affine-text-tertiary: #333 !important;
--affine-background-primary: #fff !important;
--affine-background-secondary: #fff !important;
--affine-background-tertiary: #fff !important;
}
body, [data-theme='dark'] {
color: #000 !important;
background: #fff !important;
}
body * {
color: #000 !important;
-webkit-text-fill-color: #000 !important;
}
:root {
--affine-note-shadow-box: none !important;
--affine-note-shadow-sticker: none !important;
}
}</style></head><body></body></html>`);
doc.close();
iframe.contentWindow.document
.write(`<!DOCTYPE html><html><head><style>@media print {
html, body {
@@ -94,9 +49,6 @@ export async function printToPdf(
--affine-background-primary: #fff !important;
--affine-background-secondary: #fff !important;
--affine-background-tertiary: #fff !important;
--affine-background-code-block: #f5f5f5 !important;
--affine-quote-color: #e3e3e3 !important;
--affine-border-color: #e3e3e3 !important;
}
body, [data-theme='dark'] {
color: #000 !important;
@@ -116,7 +68,7 @@ export async function printToPdf(
for (const element of document.styleSheets) {
try {
for (const cssRule of element.cssRules) {
const target = doc.styleSheets[0];
const target = iframe.contentWindow.document.styleSheets[0];
target.insertRule(cssRule.cssText, target.cssRules.length);
}
} catch (e) {
@@ -131,33 +83,12 @@ export async function printToPdf(
}
}
// Recursive function to find all canvases, including those in shadow roots
const findAllCanvases = (root: Node): HTMLCanvasElement[] => {
const canvases: HTMLCanvasElement[] = [];
const traverse = (node: Node) => {
if (node instanceof HTMLCanvasElement) {
canvases.push(node);
}
if (node instanceof HTMLElement || node instanceof ShadowRoot) {
node.childNodes.forEach(traverse);
}
if (node instanceof HTMLElement && node.shadowRoot) {
traverse(node.shadowRoot);
}
};
traverse(root);
return canvases;
};
// convert all canvas to image
const canvasImgObjectUrlMap = new Map<string, string>();
const allCanvas = findAllCanvases(rootElement);
const allCanvas = rootElement.getElementsByTagName('canvas');
let canvasKey = 1;
const canvasToKeyMap = new Map<HTMLCanvasElement, string>();
for (const canvas of allCanvas) {
const key = canvasKey.toString();
canvasToKeyMap.set(canvas, key);
canvas.dataset['printToPdfCanvasKey'] = canvasKey.toString();
canvasKey++;
const canvasImgObjectUrl = await new Promise<Blob | null>(resolve => {
try {
@@ -172,42 +103,20 @@ export async function printToPdf(
);
continue;
}
canvasImgObjectUrlMap.set(key, URL.createObjectURL(canvasImgObjectUrl));
canvasImgObjectUrlMap.set(
canvas.dataset['printToPdfCanvasKey'],
URL.createObjectURL(canvasImgObjectUrl)
);
}
// Recursive deep clone that flattens Shadow DOM into Light DOM
const deepCloneWithShadows = (node: Node): Node => {
const clone = doc.importNode(node, false);
if (
clone instanceof HTMLCanvasElement &&
node instanceof HTMLCanvasElement
) {
const key = canvasToKeyMap.get(node);
if (key) {
clone.dataset['printToPdfCanvasKey'] = key;
}
}
const appendChildren = (source: Node) => {
source.childNodes.forEach(child => {
(clone as Element).append(deepCloneWithShadows(child));
});
};
if (node instanceof HTMLElement && node.shadowRoot) {
appendChildren(node.shadowRoot);
}
appendChildren(node);
return clone;
};
const importedRoot = deepCloneWithShadows(rootElement) as HTMLDivElement;
const importedRoot = iframe.contentWindow.document.importNode(
rootElement,
true
) as HTMLDivElement;
// force light theme in print iframe
doc.documentElement.dataset.theme = 'light';
doc.body.dataset.theme = 'light';
iframe.contentWindow.document.documentElement.dataset.theme = 'light';
iframe.contentWindow.document.body.dataset.theme = 'light';
importedRoot.dataset.theme = 'light';
// draw saved canvas image to canvas
@@ -226,67 +135,17 @@ export async function printToPdf(
}
}
// Remove lazy loading from all images and force reload
const allImages = importedRoot.querySelectorAll('img');
allImages.forEach(img => {
img.removeAttribute('loading');
const src = img.getAttribute('src');
if (src) img.setAttribute('src', src);
});
// append to iframe
doc.body.append(importedRoot);
// append to iframe and print
iframe.contentWindow.document.body.append(importedRoot);
await options.beforeprint?.(iframe);
// Robust image waiting logic
const waitForImages = async (container: HTMLElement) => {
const images: HTMLImageElement[] = [];
const view = container.ownerDocument.defaultView;
if (!view) return;
const findImages = (root: Node) => {
if (root instanceof view.HTMLImageElement) {
images.push(root);
}
if (
root instanceof view.HTMLElement ||
root instanceof view.ShadowRoot
) {
root.childNodes.forEach(findImages);
}
if (root instanceof view.HTMLElement && root.shadowRoot) {
findImages(root.shadowRoot);
}
};
findImages(container);
await Promise.all(
images.map(img => {
if (img.complete) {
if (img.naturalWidth === 0) {
console.warn('Image failed to load:', img.src);
}
return Promise.resolve();
}
return new Promise(resolve => {
img.onload = resolve;
img.onerror = resolve;
});
})
);
};
await waitForImages(importedRoot);
// browser may take some time to load font or other resources
await (doc.fonts?.ready ??
new Promise<void>(resolve => {
setTimeout(() => {
resolve();
}, 1000);
}));
// browser may take some time to load font
await new Promise<void>(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
iframe.contentWindow.onafterprint = async () => {
iframe.remove();

View File

@@ -25,19 +25,19 @@
"dependencies": {
"@affine/s3-compat": "workspace:*",
"@affine/server-native": "workspace:*",
"@apollo/server": "^5.0.0",
"@apollo/server": "^4.13.0",
"@fal-ai/serverless-client": "^0.15.0",
"@google-cloud/opentelemetry-cloud-trace-exporter": "^3.0.0",
"@google-cloud/opentelemetry-resource-util": "^3.0.0",
"@nestjs-cls/transactional": "^3.2.0",
"@nestjs-cls/transactional-adapter-prisma": "^1.3.4",
"@nestjs/apollo": "^13.0.4",
"@nestjs/apollo": "^13.2.4",
"@nestjs/bullmq": "^11.0.4",
"@nestjs/common": "^11.0.21",
"@nestjs/core": "^11.1.14",
"@nestjs/graphql": "^13.0.4",
"@nestjs/platform-express": "^11.1.14",
"@nestjs/platform-socket.io": "^11.1.14",
"@nestjs/common": "^11.1.16",
"@nestjs/core": "^11.1.16",
"@nestjs/graphql": "^13.2.4",
"@nestjs/platform-express": "^11.1.16",
"@nestjs/platform-socket.io": "^11.1.16",
"@nestjs/schedule": "^6.1.1",
"@nestjs/throttler": "^6.5.0",
"@nestjs/websockets": "^11.1.14",

View File

@@ -12,10 +12,10 @@
},
"sideEffects": false,
"devDependencies": {
"@graphql-codegen/add": "^5.0.3",
"@graphql-codegen/cli": "^5.0.7",
"@graphql-codegen/typescript": "^4.1.6",
"@graphql-codegen/typescript-operations": "^4.6.1",
"@graphql-codegen/add": "^6.0.0",
"@graphql-codegen/cli": "^6.1.3",
"@graphql-codegen/typescript": "^5.0.9",
"@graphql-codegen/typescript-operations": "^5.0.9",
"@types/lodash-es": "^4.17.12",
"prettier": "^3.7.4",
"vitest": "^4.0.18"

View File

@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apollographql/apollo-ios",
"state" : {
"revision" : "3b1203b721a81f7995ee73f393aa07b43d301720",
"version" : "2.0.6"
"revision" : "d591c1dd55824867877cff4f6551fe32983d7f51",
"version" : "1.23.0"
}
},
{

View File

@@ -14,7 +14,7 @@ let package = Package(
.library(name: "AffineGraphQL", targets: ["AffineGraphQL"]),
],
dependencies: [
.package(url: "https://github.com/apollographql/apollo-ios", exact: "2.0.6"),
.package(url: "https://github.com/apollographql/apollo-ios", exact: "1.23.0"),
],
targets: [
.target(

View File

@@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.package(path: "../AffineGraphQL"),
.package(path: "../AffineResources"),
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "2.0.6"),
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.23.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.4.0"),
.package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1"),
.package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "6.2.0"),

View File

@@ -54,22 +54,13 @@ export class I18n extends Entity {
constructor(private readonly cache: GlobalCache) {
super();
this.i18n.on('languageChanged', (language: Language) => {
this.applyDocumentLanguage(language);
document.documentElement.lang = language;
this.cache.set('i18n_lng', language);
});
}
init() {
const language = this.currentLanguageKey$.value ?? 'en';
this.applyDocumentLanguage(language);
this.changeLanguage(language);
}
private applyDocumentLanguage(language: Language) {
document.documentElement.lang = language;
document.documentElement.dir = SUPPORTED_LANGUAGES[language]?.rtl
? 'rtl'
: 'ltr';
this.changeLanguage(this.currentLanguageKey$.value ?? 'en');
}
changeLanguage = effect(

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,6 @@ export const SUPPORTED_LANGUAGES: Record<
name: string;
originalName: string;
flagEmoji: string;
rtl?: boolean;
resource:
| LanguageResource
| (() => Promise<{ default: Partial<LanguageResource> }>);
@@ -150,21 +149,18 @@ export const SUPPORTED_LANGUAGES: Record<
name: 'Urdu',
originalName: 'اردو',
flagEmoji: '🇵🇰',
rtl: true,
resource: () => import('./ur.json'),
},
ar: {
name: 'Arabic',
originalName: 'العربية',
flagEmoji: '🇸🇦',
rtl: true,
resource: () => import('./ar.json'),
},
fa: {
name: 'Persian',
originalName: 'فارسی',
flagEmoji: '🇮🇷',
rtl: true,
resource: () => import('./fa.json'),
},
uk: {

View File

@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" dir="ltr">
<html lang="en">
<head>
<meta charset="utf-8" />
<meta

1382
yarn.lock

File diff suppressed because it is too large Load Diff