mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 21:38:44 +08:00
feat(core): support Notion markdown zip imports (#14910)
## What changed - Markdown zip imports now resolve local `.md` links like `./test/2.md` into AFFiNE linked-page references when the target document exists in the same archive. - Added Notion Markdown `.zip` import support in the desktop import dialog, including nested zip traversal, Notion title extraction, hash-stripped folder names, attachments, and folder hierarchy integration. - Added i18n entries and adapter coverage for standard Markdown zip links and Notion Markdown zip imports. ## Why Markdown and Notion exports often contain links between notes using relative `.md` paths. Keeping those as plain URLs makes imported workspaces harder to navigate, so the importer now preassigns document ids and rewrites resolvable archive-local markdown links into linked pages. ## Notes The markdown zip folder hierarchy implementation now comes from latest `origin/canary`, so this PR only layers relative-link resolution and Notion Markdown zip support on top of that upstream behavior. ## Validation - `yarn vitest --run blocksuite/affine/all/src/__tests__/adapters/markdown.unit.spec.ts` - `yarn tsc -b blocksuite/affine/all/tsconfig.json --verbose` - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for importing Notion Markdown exports in `.zip` format, including subpages, attachments, and nested folders. * Internal links inside imported Markdown now resolve correctly between pages, preserving link text when available. * The import dialog now includes a dedicated “Notion (Markdown, .zip)” option. * **Bug Fixes** * Improved filename handling so non-Latin characters in ZIP imports are preserved correctly. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com> Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
@@ -231,6 +231,7 @@ type ImportType =
|
||||
| 'markdown'
|
||||
| 'markdownZip'
|
||||
| 'notion'
|
||||
| 'notionMarkdown'
|
||||
| 'obsidian'
|
||||
| 'bear'
|
||||
| 'snapshot'
|
||||
@@ -318,6 +319,17 @@ const importOptions = [
|
||||
testId: 'editor-option-menu-import-notion',
|
||||
type: 'notion' as ImportType,
|
||||
},
|
||||
{
|
||||
key: 'notionMarkdown',
|
||||
label: 'com.affine.import.notion-markdown',
|
||||
prefixIcon: <NotionIcon color={cssVar('black')} width={20} height={20} />,
|
||||
suffixIcon: (
|
||||
<HelpIcon color={cssVarV2('icon/primary')} width={20} height={20} />
|
||||
),
|
||||
suffixTooltip: 'com.affine.import.notion-markdown.tooltip',
|
||||
testId: 'editor-option-menu-import-notion-markdown',
|
||||
type: 'notionMarkdown' as ImportType,
|
||||
},
|
||||
{
|
||||
key: 'obsidian',
|
||||
label: 'com.affine.import.obsidian',
|
||||
@@ -510,6 +522,42 @@ const importConfigs: Record<ImportType, ImportConfig> = {
|
||||
};
|
||||
},
|
||||
},
|
||||
notionMarkdown: {
|
||||
fileOptions: { acceptType: 'Zip', multiple: false },
|
||||
importFunction: async (
|
||||
docCollection,
|
||||
files,
|
||||
_handleImportAffineFile,
|
||||
organizeService,
|
||||
explorerIconService
|
||||
) => {
|
||||
const file = files.length === 1 ? files[0] : null;
|
||||
if (!file) {
|
||||
throw new Error('Expected a single zip file for notionMarkdown import');
|
||||
}
|
||||
const { docIds, folderHierarchy } =
|
||||
await MarkdownTransformer.importNotionMarkdownZip({
|
||||
collection: docCollection,
|
||||
schema: getAFFiNEWorkspaceSchema(),
|
||||
imported: file,
|
||||
extensions: getStoreManager().config.init().value.get('store'),
|
||||
});
|
||||
|
||||
const rootFolderId =
|
||||
folderHierarchy && organizeService
|
||||
? applyFolderHierarchy(
|
||||
organizeService,
|
||||
folderHierarchy,
|
||||
explorerIconService
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
docIds,
|
||||
rootFolderId,
|
||||
};
|
||||
},
|
||||
},
|
||||
obsidian: {
|
||||
fileOptions: { acceptType: 'Directory', multiple: false },
|
||||
importFunction: async (
|
||||
|
||||
Reference in New Issue
Block a user