Skip to content

Commit

Permalink
feat(core): export doc with adapter extension
Browse files Browse the repository at this point in the history
  • Loading branch information
donteatfriedrice committed Dec 23, 2024
1 parent 02aeb89 commit a2785b0
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -48,6 +49,7 @@ const CommonBlockSpecs: ExtensionType[] = [
EmbedLinkedDocBlockSpec,
// special
CustomAttachmentBlockSpec,
AdapterFactoryExtensions,
].flat();

export const DefaultBlockSpecs: ExtensionType[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand All @@ -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]);
Expand Down

0 comments on commit a2785b0

Please sign in to comment.