diff --git a/apps/server/src/integrations/import/services/import-attachment.service.ts b/apps/server/src/integrations/import/services/import-attachment.service.ts index bba517d0..a797ecc6 100644 --- a/apps/server/src/integrations/import/services/import-attachment.service.ts +++ b/apps/server/src/integrations/import/services/import-attachment.service.ts @@ -14,6 +14,7 @@ import { getAttachmentFolderPath } from '../../../core/attachment/attachment.uti import { AttachmentType } from '../../../core/attachment/attachment.constants'; import { unwrapFromParagraph } from '../utils/import-formatter'; import { resolveRelativeAttachmentPath } from '../utils/import.utils'; +import { imageDimensionsFromData } from 'image-dimensions'; import { load } from 'cheerio'; import pLimit from 'p-limit'; import { InjectQueue } from '@nestjs/bullmq'; @@ -271,15 +272,37 @@ export class ImportAttachmentService { continue; } - const { attachmentId, apiFilePath } = processFile(relPath); + const { attachmentId, apiFilePath, abs } = processFile(relPath); - const width = $img.attr('width') ?? '100%'; + let width = $img.attr('width'); + const height = $img.attr('height'); const align = $img.attr('data-align') ?? 'center'; + if (!width) { + try { + const buf = await fs.readFile(abs); + const natural = imageDimensionsFromData(new Uint8Array(buf)); + if (natural) { + width = height + ? String( + Math.round((natural.width / natural.height) * Number(height)), + ) + : String(natural.width); + } + } catch { + /* empty */ + } + + if (!width) { + width = '600'; + } + } + $img .attr('src', apiFilePath) .attr('data-attachment-id', attachmentId) .attr('width', width) + .attr('height', height) .attr('data-align', align); unwrapFromParagraph($, $img); @@ -420,7 +443,7 @@ export class ImportAttachmentService { const { attachmentId, apiFilePath, abs } = processFile(relPath); const fileName = path.basename(abs); - const width = $oldDiv.attr('data-width') || '100%'; + const width = $oldDiv.attr('data-width') || '600'; const align = $oldDiv.attr('data-align') || 'center'; const $newDiv = $('
') @@ -460,7 +483,7 @@ export class ImportAttachmentService { .attr('data-type', 'drawio') .attr('data-src', drawioSvg.apiFilePath) .attr('data-title', 'diagram') - .attr('data-width', '100%') + .attr('data-width', '600') .attr('data-align', 'center') .attr('data-attachment-id', drawioSvg.attachmentId); @@ -531,7 +554,9 @@ export class ImportAttachmentService { // Post-process DOM elements to add file sizes after uploads complete // This avoids blocking file operations during initial DOM processing - const elementsNeedingSize = $('[data-attachment-id]:not([data-attachment-size])'); + const elementsNeedingSize = $( + '[data-attachment-id]:not([data-attachment-size])', + ); for (const element of elementsNeedingSize.toArray()) { const $el = $(element); const attachmentId = $el.attr('data-attachment-id'); diff --git a/apps/server/src/integrations/import/utils/import-formatter.ts b/apps/server/src/integrations/import/utils/import-formatter.ts index 360e22de..4f6a1a80 100644 --- a/apps/server/src/integrations/import/utils/import-formatter.ts +++ b/apps/server/src/integrations/import/utils/import-formatter.ts @@ -99,6 +99,15 @@ export function defaultHtmlFormatter($: CheerioAPI, $root: Cheerio) { }); } +const COLUMN_LAYOUTS = [ + '', + '', + 'two_equal', + 'three_equal', + 'four_equal', + 'five_equal', +] as const; + export function notionFormatter($: CheerioAPI, $root: Cheerio) { // remove page header icon and cover image $root.find('.page-header-icon').remove(); @@ -109,6 +118,31 @@ export function notionFormatter($: CheerioAPI, $root: Cheerio) { if (!$(el).text().trim()) $(el).remove(); }); + // columns + $root.find('div.column-list').each((_, el) => { + const $list = $(el); + const $cols = $list.find('div.column'); + + if ($cols.length <= 1) { + $list.replaceWith($cols.html() || ''); + return; + } + + const layout = COLUMN_LAYOUTS[$cols.length] ?? 'two_equal'; + let cells = ''; + $cols.each((_, col) => { + const $col = $(col); + $col.children('div[style*="display:contents"]').each((_, wrapper) => { + $(wrapper).replaceWith($(wrapper).html() || ''); + }); + cells += `
${$col.html()}
`; + }); + + $list.replaceWith( + `
${cells}
`, + ); + }); + // block math → mathBlock $root.find('figure.equation').each((_: any, fig: any) => { const $fig = $(fig);