mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 18:46:19 +08:00
feat(core): export doc with adapter extension (#9226)
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
import { AIChatBlockSpec } from '@affine/core/blocksuite/presets/blocks/ai-chat-block';
|
||||
import type { ExtensionType } from '@blocksuite/affine/block-std';
|
||||
import {
|
||||
AdapterFactoryExtensions,
|
||||
BookmarkBlockSpec,
|
||||
CodeBlockSpec,
|
||||
DatabaseBlockSpec,
|
||||
@@ -48,6 +49,7 @@ const CommonBlockSpecs: ExtensionType[] = [
|
||||
EmbedLinkedDocBlockSpec,
|
||||
// special
|
||||
CustomAttachmentBlockSpec,
|
||||
AdapterFactoryExtensions,
|
||||
].flat();
|
||||
|
||||
export const DefaultBlockSpecs: ExtensionType[] = [
|
||||
|
||||
@@ -6,15 +6,23 @@ import {
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import type { BlockStdScope } from '@blocksuite/affine/block-std';
|
||||
import {
|
||||
createAssetsArchive,
|
||||
docLinkBaseURLMiddleware,
|
||||
download,
|
||||
embedSyncedDocMiddleware,
|
||||
ExportManager,
|
||||
HtmlAdapterFactoryIdentifier,
|
||||
HtmlTransformer,
|
||||
MarkdownAdapterFactoryIdentifier,
|
||||
MarkdownTransformer,
|
||||
printToPdf,
|
||||
titleMiddleware,
|
||||
ZipTransformer,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import type { AffineEditorContainer } from '@blocksuite/affine/presets';
|
||||
import type { Doc } from '@blocksuite/affine/store';
|
||||
import { type Doc, Job } from '@blocksuite/affine/store';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { nanoid } from 'nanoid';
|
||||
@@ -29,6 +37,90 @@ interface ExportHandlerOptions {
|
||||
type: ExportType;
|
||||
}
|
||||
|
||||
interface AdapterResult {
|
||||
file: string;
|
||||
assetsIds: string[];
|
||||
}
|
||||
|
||||
type AdapterFactoryIdentifier =
|
||||
| typeof HtmlAdapterFactoryIdentifier
|
||||
| typeof MarkdownAdapterFactoryIdentifier;
|
||||
|
||||
interface AdapterConfig {
|
||||
identifier: AdapterFactoryIdentifier;
|
||||
fileExtension: string; // file extension need to be lower case with dot prefix, e.g. '.md', '.txt', '.html'
|
||||
contentType: string;
|
||||
indexFileName: string;
|
||||
}
|
||||
|
||||
async function exportDoc(doc: Doc, std: BlockStdScope, config: AdapterConfig) {
|
||||
const job = new Job({
|
||||
collection: doc.collection,
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware,
|
||||
titleMiddleware,
|
||||
embedSyncedDocMiddleware('content'),
|
||||
],
|
||||
});
|
||||
|
||||
const adapterFactory = std.provider.get(config.identifier);
|
||||
const adapter = adapterFactory.get(job);
|
||||
const result = (await adapter.fromDoc(doc)) as AdapterResult;
|
||||
|
||||
if (!result || (!result.file && !result.assetsIds.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const docTitle = doc.meta?.title || 'Untitled';
|
||||
const contentBlob = new Blob([result.file], { type: config.contentType });
|
||||
|
||||
let downloadBlob: Blob;
|
||||
let name: string;
|
||||
|
||||
if (result.assetsIds.length > 0) {
|
||||
if (!job.assets) {
|
||||
throw new Error('No assets found');
|
||||
}
|
||||
const zip = await createAssetsArchive(job.assets, result.assetsIds);
|
||||
await zip.file(config.indexFileName, contentBlob);
|
||||
downloadBlob = await zip.generate();
|
||||
name = `${docTitle}.zip`;
|
||||
} else {
|
||||
downloadBlob = contentBlob;
|
||||
name = `${docTitle}${config.fileExtension}`;
|
||||
}
|
||||
|
||||
download(downloadBlob, name);
|
||||
}
|
||||
|
||||
async function exportToHtml(doc: Doc, std?: BlockStdScope) {
|
||||
if (!std) {
|
||||
// If std is not provided, we use the default export method
|
||||
await HtmlTransformer.exportDoc(doc);
|
||||
} else {
|
||||
await exportDoc(doc, std, {
|
||||
identifier: HtmlAdapterFactoryIdentifier,
|
||||
fileExtension: '.html',
|
||||
contentType: 'text/html',
|
||||
indexFileName: 'index.html',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function exportToMarkdown(doc: Doc, std?: BlockStdScope) {
|
||||
if (!std) {
|
||||
// If std is not provided, we use the default export method
|
||||
await MarkdownTransformer.exportDoc(doc);
|
||||
} else {
|
||||
await exportDoc(doc, std, {
|
||||
identifier: MarkdownAdapterFactoryIdentifier,
|
||||
fileExtension: '.md',
|
||||
contentType: 'text/plain',
|
||||
indexFileName: 'index.md',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function exportHandler({
|
||||
page,
|
||||
type,
|
||||
@@ -40,10 +132,10 @@ async function exportHandler({
|
||||
});
|
||||
switch (type) {
|
||||
case 'html':
|
||||
await HtmlTransformer.exportDoc(page);
|
||||
await exportToHtml(page, editorRoot?.std);
|
||||
return;
|
||||
case 'markdown':
|
||||
await MarkdownTransformer.exportDoc(page);
|
||||
await exportToMarkdown(page, editorRoot?.std);
|
||||
return;
|
||||
case 'snapshot':
|
||||
await ZipTransformer.exportDocs(page.collection, [page]);
|
||||
|
||||
Reference in New Issue
Block a user