From af882d4fc2858911a5c3c879b4e0d23ee0e8e630 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 08:33:19 -0700 Subject: [PATCH 01/20] fix list indentation --- src/formats/bear-bear2bk.ts | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 249538cf..29674b21 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -119,6 +119,7 @@ export class Bear2bkImporter extends FormatImporter { ctx.status('Importing note ' + mdFilename); let mdContent = await entry.readText(); mdContent = this.removeMarkdownHeader(mdFilename, mdContent); + mdContent = this.fixListIndentation(mdContent); const assetMatches = [...mdContent.matchAll(assetMatcher)]; if (assetMatches.length > 0) { @@ -341,4 +342,54 @@ export class Bear2bkImporter extends FormatImporter { ? mdContent.substring(idx + 1) : ''; } + + private fixListIndentation(text: string): string { + const hasTrailingNewline = text.endsWith('\n'); + const lines = hasTrailingNewline ? text.slice(0, -1).split('\n') : text.split('\n'); + + const out: string[] = []; + let inFrontmatter = false; + let inCode = false; + + for (let i = 0; i < lines.length; i += 1) { + let line = lines[i]; + + if (i === 0 && line.trim() === '---') { + inFrontmatter = true; + out.push(line); + continue; + } + + if (inFrontmatter) { + out.push(line); + if (line.trim() === '---') { + inFrontmatter = false; + } + continue; + } + + const stripped = line.trimStart(); + if (stripped.startsWith('```')) { + inCode = !inCode; + out.push(line); + continue; + } + + if (inCode) { + out.push(line); + continue; + } + + const match = line.match(/^( +)([-*+]\s|\d+\.)/); + if (match) { + const spaces = match[1]; + const newIndent = ' '.repeat(spaces.length * 2); + line = newIndent + line.slice(spaces.length); + } + + out.push(line); + } + + return out.join('\n') + (hasTrailingNewline ? '\n' : ''); + } } From d41746299940b43c369dd341943f8fb6add076c2 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 09:23:54 -0700 Subject: [PATCH 02/20] don't create invalid tags --- src/formats/bear-bear2bk.ts | 73 ++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 29674b21..cb4fa942 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -48,32 +48,61 @@ export class Bear2bkImporter extends FormatImporter { ); } + private normalizeSimpleTag(rawTag: string): string | null { + if (!rawTag) { + return null; + } + + const alphaStart = /^[A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ]/; + const nonAlphaStart = /^[0-9_\-]/; + const invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; + + if (alphaStart.test(rawTag)) { + let cleanTag = rawTag.replace(invalidChar, '_'); + cleanTag = cleanTag.replace(/_+/g, '_'); + return cleanTag; + } + + if (nonAlphaStart.test(rawTag)) { + if (invalidChar.test(rawTag)) { + return null; + } + if (/^\d+$/.test(rawTag)) { + return null; + } + return rawTag; + } + + return null; + } + private extractTagsFromContent(content: string): string[] { const tags = new Set(); + const isNumericOnly = (value: string) => /^\d+$/.test(value); + const tagCandidateRegex = /(? { @@ -145,10 +174,12 @@ export class Bear2bkImporter extends FormatImporter { }); // Remove special characters in simple tags - mdContent = mdContent.replace(/#([^0-9\s#]+)/g, (_match, tag) => { - let cleanTag = tag.replace(/[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_/\-]/g, '_'); - cleanTag = cleanTag.replace(/_+/g, '_'); // collapse multiple underscores - return '#' + cleanTag; + mdContent = mdContent.replace(/(? { + const normalizedTag = this.normalizeSimpleTag(rawTag); + if (!normalizedTag) { + return match; + } + return '#' + normalizedTag; }); // Extract tags from content From 6fd6870bc03011987bbc0d685754afb44ed1421e Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 09:29:08 -0700 Subject: [PATCH 03/20] prevent tags from code block contents --- src/formats/bear-bear2bk.ts | 69 +++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index cb4fa942..aec333b1 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -76,31 +76,54 @@ export class Bear2bkImporter extends FormatImporter { return null; } + private transformOutsideCodeBlocks(content: string, transformLine: (line: string) => string): string { + const hasTrailingNewline = content.endsWith('\n'); + const lines = hasTrailingNewline ? content.slice(0, -1).split('\n') : content.split('\n'); + let inCode = false; + const out: string[] = []; + + for (const line of lines) { + const trimmed = line.trimStart(); + if (trimmed.startsWith('```')) { + inCode = !inCode; + out.push(line); + continue; + } + + out.push(inCode ? line : transformLine(line)); + } + + return out.join('\n') + (hasTrailingNewline ? '\n' : ''); + } + private extractTagsFromContent(content: string): string[] { const tags = new Set(); const isNumericOnly = (value: string) => /^\d+$/.test(value); const tagCandidateRegex = /(? { + let match; + while ((match = tagCandidateRegex.exec(line)) !== null) { + const rawTag = match[1].trim(); + const normalizedTag = this.normalizeSimpleTag(rawTag); + if (!normalizedTag) { + continue; + } - if (this.flattenTags && normalizedTag.includes('/')) { - const parts = normalizedTag.split('/'); - for (const part of parts) { - if (part !== '' && !isNumericOnly(part)) { - tags.add(part); + if (this.flattenTags && normalizedTag.includes('/')) { + const parts = normalizedTag.split('/'); + for (const part of parts) { + if (part !== '' && !isNumericOnly(part)) { + tags.add(part); + } } } + else if (!isNumericOnly(normalizedTag)) { + tags.add(normalizedTag); + } } - else if (!isNumericOnly(normalizedTag)) { - tags.add(normalizedTag); - } - } + return line; + }); return Array.from(tags); } @@ -174,12 +197,14 @@ export class Bear2bkImporter extends FormatImporter { }); // Remove special characters in simple tags - mdContent = mdContent.replace(/(? { - const normalizedTag = this.normalizeSimpleTag(rawTag); - if (!normalizedTag) { - return match; - } - return '#' + normalizedTag; + mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { + return line.replace(/(? { + const normalizedTag = this.normalizeSimpleTag(rawTag); + if (!normalizedTag) { + return match; + } + return '#' + normalizedTag; + }); }); // Extract tags from content From d4542edcdec13faf310c0b9bf0a03a6d491a46d6 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 09:40:26 -0700 Subject: [PATCH 04/20] prevent tags from header lines --- src/formats/bear-bear2bk.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index aec333b1..c18d9ee8 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -192,8 +192,10 @@ export class Bear2bkImporter extends FormatImporter { } // Replace spaces in enclosed tags with underscores and make them classic tags - mdContent = mdContent.replace(/#([^\n#]+?[^\s])#/g, (_match, tag) => { // require non-space before closing to avoid using next tag's opening # - return '#' + tag.replace(/\s+/g, '_'); + mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { + return line.replace(/#(?!\s)([^\n#]*?\S)#/g, (_match, tag) => { + return '#' + tag.replace(/\s+/g, '_'); + }); }); // Remove special characters in simple tags From bd61d8c17dc85ae9206340f6e1a047afaa19e5eb Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 09:55:45 -0700 Subject: [PATCH 05/20] prevent incorrect tags from text after tags with closing # --- src/formats/bear-bear2bk.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index c18d9ee8..31d9616b 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -193,7 +193,9 @@ export class Bear2bkImporter extends FormatImporter { // Replace spaces in enclosed tags with underscores and make them classic tags mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(/#(?!\s)([^\n#]*?\S)#/g, (_match, tag) => { + return line.replace(/#(?!\s)([^\n#]*?\S)#(?=\S)/g, (_match, tag) => { + return '#' + tag.replace(/\s+/g, '_') + ' '; + }).replace(/#(?!\s)([^\n#]*?\S)#/g, (_match, tag) => { return '#' + tag.replace(/\s+/g, '_'); }); }); From 473396e3b4325e1f453ff9d5031343fe24017a44 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 10:35:14 -0700 Subject: [PATCH 06/20] escape hex color "tags" --- src/formats/bear-bear2bk.ts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 31d9616b..69935b15 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -53,6 +53,10 @@ export class Bear2bkImporter extends FormatImporter { return null; } + if (this.isHexColorTag(rawTag)) { + return null; + } + const alphaStart = /^[A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ]/; const nonAlphaStart = /^[0-9_\-]/; const invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; @@ -76,6 +80,18 @@ export class Bear2bkImporter extends FormatImporter { return null; } + private isHexColorTag(rawTag: string): boolean { + return /^[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(rawTag); + } + + private escapeHexColorTags(content: string): string { + return this.transformOutsideCodeBlocks(content, (line) => { + return line.replace(/(? { + return `\\#${hex}`; + }); + }); + } + private transformOutsideCodeBlocks(content: string, transformLine: (line: string) => string): string { const hasTrailingNewline = content.endsWith('\n'); const lines = hasTrailingNewline ? content.slice(0, -1).split('\n') : content.split('\n'); @@ -171,6 +187,7 @@ export class Bear2bkImporter extends FormatImporter { ctx.status('Importing note ' + mdFilename); let mdContent = await entry.readText(); mdContent = this.removeMarkdownHeader(mdFilename, mdContent); + mdContent = this.escapeHexColorTags(mdContent); mdContent = this.fixListIndentation(mdContent); const assetMatches = [...mdContent.matchAll(assetMatcher)]; @@ -193,9 +210,15 @@ export class Bear2bkImporter extends FormatImporter { // Replace spaces in enclosed tags with underscores and make them classic tags mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(/#(?!\s)([^\n#]*?\S)#(?=\S)/g, (_match, tag) => { + return line.replace(/#(?!\s)([^\n#]*?\S)#(?=\S)/g, (match, tag) => { + if (this.isHexColorTag(tag)) { + return match; + } return '#' + tag.replace(/\s+/g, '_') + ' '; - }).replace(/#(?!\s)([^\n#]*?\S)#/g, (_match, tag) => { + }).replace(/#(?!\s)([^\n#]*?\S)#/g, (match, tag) => { + if (this.isHexColorTag(tag)) { + return match; + } return '#' + tag.replace(/\s+/g, '_'); }); }); From a68c67e03043ed58df823ea48d6231d308cbd65d Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 10:43:25 -0700 Subject: [PATCH 07/20] refactor to precompile reused regex --- src/formats/bear-bear2bk.ts | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 69935b15..552cf9bc 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -23,6 +23,19 @@ export class Bear2bkImporter extends FormatImporter { private flattenTags: boolean = false; private storeId: boolean = false; + private static readonly alphaStart = /^[A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ]/; + private static readonly nonAlphaStart = /^[0-9_\-]/; + private static readonly invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; + private static readonly hexColorTag = /^[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/; + private static readonly hexColorInline = /(? { - return line.replace(/(? { + return line.replace(Bear2bkImporter.hexColorInline, (_match, hex) => { return `\\#${hex}`; }); }); @@ -114,11 +125,12 @@ export class Bear2bkImporter extends FormatImporter { private extractTagsFromContent(content: string): string[] { const tags = new Set(); - const isNumericOnly = (value: string) => /^\d+$/.test(value); - const tagCandidateRegex = /(? Bear2bkImporter.numericOnly.test(value); + const tagCandidateRegex = Bear2bkImporter.tagCandidate; this.transformOutsideCodeBlocks(content, (line) => { let match; + tagCandidateRegex.lastIndex = 0; while ((match = tagCandidateRegex.exec(line)) !== null) { const rawTag = match[1].trim(); const normalizedTag = this.normalizeSimpleTag(rawTag); @@ -164,7 +176,7 @@ export class Bear2bkImporter extends FormatImporter { let outputFolder = folder; // match 1: assets/something.jpg - const assetMatcher = new RegExp('\\[[^\\]]*\\]\\((assets/[^\\)]+)\\)', 'gm'); + const assetMatcher = Bear2bkImporter.assetMatcher; const archiveFolder = await this.createFolders(`${folder.path}/archive`); const trashFolder = await this.createFolders(`${folder.path}/trash`); @@ -210,12 +222,12 @@ export class Bear2bkImporter extends FormatImporter { // Replace spaces in enclosed tags with underscores and make them classic tags mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(/#(?!\s)([^\n#]*?\S)#(?=\S)/g, (match, tag) => { + return line.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { if (this.isHexColorTag(tag)) { return match; } return '#' + tag.replace(/\s+/g, '_') + ' '; - }).replace(/#(?!\s)([^\n#]*?\S)#/g, (match, tag) => { + }).replace(Bear2bkImporter.enclosedTag, (match, tag) => { if (this.isHexColorTag(tag)) { return match; } @@ -225,7 +237,7 @@ export class Bear2bkImporter extends FormatImporter { // Remove special characters in simple tags mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(/(? { + return line.replace(Bear2bkImporter.tagCandidate, (match, rawTag) => { const normalizedTag = this.normalizeSimpleTag(rawTag); if (!normalizedTag) { return match; @@ -344,7 +356,7 @@ export class Bear2bkImporter extends FormatImporter { mtime: metadata?.mtime, }; await this.vault.process(file, (mdContent) => { - return mdContent.replace(/bear:\/\/x-callback-url\/open-note\?id=([A-Z0-9\-]+)/g, + return mdContent.replace(Bear2bkImporter.bearLinkMatcher, (match, noteId) => { const noteTitle = idMapping[noteId]?.filename; if (noteTitle) { From ceb86abfdf40dbab7b80cf38c0d0cb2f2adecaa6 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 10:45:26 -0700 Subject: [PATCH 08/20] merge transformOutsideCodeBlocks passes --- src/formats/bear-bear2bk.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 552cf9bc..f7cb1023 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -220,9 +220,9 @@ export class Bear2bkImporter extends FormatImporter { } } - // Replace spaces in enclosed tags with underscores and make them classic tags + // Replace spaces in enclosed tags, then normalize simple tags (single pass) mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { + const enclosedNormalized = line.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { if (this.isHexColorTag(tag)) { return match; } @@ -233,11 +233,8 @@ export class Bear2bkImporter extends FormatImporter { } return '#' + tag.replace(/\s+/g, '_'); }); - }); - // Remove special characters in simple tags - mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(Bear2bkImporter.tagCandidate, (match, rawTag) => { + return enclosedNormalized.replace(Bear2bkImporter.tagCandidate, (match, rawTag) => { const normalizedTag = this.normalizeSimpleTag(rawTag); if (!normalizedTag) { return match; From 49a6a6df341f82232c4b4526ed953ab845bba92e Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 10:53:38 -0700 Subject: [PATCH 09/20] combine escapeHexColorTags and tag normalization into a single pass --- src/formats/bear-bear2bk.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index f7cb1023..f1666056 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -95,14 +95,6 @@ export class Bear2bkImporter extends FormatImporter { return Bear2bkImporter.hexColorTag.test(rawTag); } - private escapeHexColorTags(content: string): string { - return this.transformOutsideCodeBlocks(content, (line) => { - return line.replace(Bear2bkImporter.hexColorInline, (_match, hex) => { - return `\\#${hex}`; - }); - }); - } - private transformOutsideCodeBlocks(content: string, transformLine: (line: string) => string): string { const hasTrailingNewline = content.endsWith('\n'); const lines = hasTrailingNewline ? content.slice(0, -1).split('\n') : content.split('\n'); @@ -199,7 +191,6 @@ export class Bear2bkImporter extends FormatImporter { ctx.status('Importing note ' + mdFilename); let mdContent = await entry.readText(); mdContent = this.removeMarkdownHeader(mdFilename, mdContent); - mdContent = this.escapeHexColorTags(mdContent); mdContent = this.fixListIndentation(mdContent); const assetMatches = [...mdContent.matchAll(assetMatcher)]; @@ -220,9 +211,12 @@ export class Bear2bkImporter extends FormatImporter { } } - // Replace spaces in enclosed tags, then normalize simple tags (single pass) + // Escape hex color tags, normalize enclosed tags, then normalize simple tags (single pass) mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - const enclosedNormalized = line.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { + const hexEscaped = line.replace(Bear2bkImporter.hexColorInline, (_match, hex) => { + return `\\#${hex}`; + }); + const enclosedNormalized = hexEscaped.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { if (this.isHexColorTag(tag)) { return match; } From 69d25a25b674c0b46fca3e53af63d010198dc254 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 11:17:31 -0700 Subject: [PATCH 10/20] rename method --- src/formats/bear-bear2bk.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index f1666056..8e0de4f2 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -257,7 +257,7 @@ export class Bear2bkImporter extends FormatImporter { await this.updateNoteFrontmatter(metadata, file, tags); } if (metadata?.ctime && metadata?.mtime) { - await this.modifFileTimestamps(metadata, file); + await this.modifyFileTimestamps(metadata, file); } idMapping[metadata?.id] = { @@ -331,7 +331,7 @@ export class Bear2bkImporter extends FormatImporter { }, writeOptions); } - private async modifFileTimestamps(metaData: Metadata, file: TFile) { + private async modifyFileTimestamps(metaData: Metadata, file: TFile) { const writeOptions: DataWriteOptions = { ctime: metaData.ctime, mtime: metaData.mtime, From 48a3aa734913502155a2d3146774cceab36018f2 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 11:45:08 -0700 Subject: [PATCH 11/20] fix support for multi-word tags --- src/formats/bear-bear2bk.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 8e0de4f2..d60f6a5d 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -91,6 +91,15 @@ export class Bear2bkImporter extends FormatImporter { return null; } + private normalizeEnclosedTag(tag: string, match: string, addTrailingSpace: boolean): string { + const normalizedSingle = tag.replace(/\s+/g, '_'); + const normalized = this.normalizeSimpleTag(normalizedSingle); + if (!normalized) { + return match; + } + return '#' + normalized + (addTrailingSpace ? ' ' : ''); + } + private isHexColorTag(rawTag: string): boolean { return Bear2bkImporter.hexColorTag.test(rawTag); } @@ -220,12 +229,12 @@ export class Bear2bkImporter extends FormatImporter { if (this.isHexColorTag(tag)) { return match; } - return '#' + tag.replace(/\s+/g, '_') + ' '; + return this.normalizeEnclosedTag(tag, match, true); }).replace(Bear2bkImporter.enclosedTag, (match, tag) => { if (this.isHexColorTag(tag)) { return match; } - return '#' + tag.replace(/\s+/g, '_'); + return this.normalizeEnclosedTag(tag, match, false); }); return enclosedNormalized.replace(Bear2bkImporter.tagCandidate, (match, rawTag) => { From ba3fdbef3f65e0b28ce554f5617d1be6fcfac1c7 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 11:52:04 -0700 Subject: [PATCH 12/20] ignore potential tags in inline code --- src/formats/bear-bear2bk.ts | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index d60f6a5d..15a7dd8d 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -118,12 +118,47 @@ export class Bear2bkImporter extends FormatImporter { continue; } - out.push(inCode ? line : transformLine(line)); + if (inCode) { + out.push(line); + continue; + } + + out.push(this.transformOutsideInlineCode(line, transformLine)); } return out.join('\n') + (hasTrailingNewline ? '\n' : ''); } + private transformOutsideInlineCode(line: string, transformLine: (line: string) => string): string { + let inInline = false; + let current = ''; + let result = ''; + + for (let i = 0; i < line.length; i += 1) { + const ch = line[i]; + if (ch === '`') { + if (!inInline) { + result += transformLine(current); + current = ''; + inInline = true; + } + else { + result += '`' + current + '`'; + current = ''; + inInline = false; + } + continue; + } + current += ch; + } + + if (inInline) { + return line; + } + + return result + transformLine(current); + } + private extractTagsFromContent(content: string): string[] { const tags = new Set(); const isNumericOnly = (value: string) => Bear2bkImporter.numericOnly.test(value); From 9531aa27c87251b064a418b30bf6cf5f20b6f34d Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 12:10:14 -0700 Subject: [PATCH 13/20] fix code blocks in lists --- src/formats/bear-bear2bk.ts | 57 ++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 15a7dd8d..0dfb342e 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -480,6 +480,33 @@ export class Bear2bkImporter extends FormatImporter { const out: string[] = []; let inFrontmatter = false; let inCode = false; + let codeIndentSourceBase: number | null = null; + let codeIndentTargetBase: number | null = null; + + const countLeadingSpaces = (value: string): number => { + let count = 0; + while (count < value.length && value[count] === ' ') { + count += 1; + } + return count; + }; + + const adjustCodeIndent = (lineValue: string, sourceBase: number, targetBase: number): string => { + const leadingSpaces = countLeadingSpaces(lineValue); + if (leadingSpaces < sourceBase) { + return lineValue; + } + const newIndent = ' '.repeat(targetBase + (leadingSpaces - sourceBase)); + return newIndent + lineValue.slice(leadingSpaces); + }; + + const listFenceMatch = (lineValue: string): { indent: number; marker: string; fence: string } | null => { + const match = lineValue.match(/^(\s*)([-*+]\s|\d+\.\s)(```.*)$/); + if (!match) { + return null; + } + return { indent: match[1].length, marker: match[2], fence: match[3] }; + }; for (let i = 0; i < lines.length; i += 1) { let line = lines[i]; @@ -498,14 +525,42 @@ export class Bear2bkImporter extends FormatImporter { continue; } + const listFence = listFenceMatch(line); const stripped = line.trimStart(); - if (stripped.startsWith('```')) { + if (listFence || stripped.startsWith('```')) { + if (listFence) { + const targetIndent = listFence.indent * 2; + line = ' '.repeat(targetIndent) + listFence.marker + listFence.fence; + if (!inCode) { + codeIndentSourceBase = listFence.indent + listFence.marker.length; + codeIndentTargetBase = targetIndent + listFence.marker.length; + } + else { + codeIndentSourceBase = null; + codeIndentTargetBase = null; + } + } + else { + const leadingSpaces = countLeadingSpaces(line); + line = ' '.repeat(leadingSpaces * 2) + line.slice(leadingSpaces); + if (!inCode) { + codeIndentSourceBase = leadingSpaces; + codeIndentTargetBase = leadingSpaces * 2; + } + else { + codeIndentSourceBase = null; + codeIndentTargetBase = null; + } + } inCode = !inCode; out.push(line); continue; } if (inCode) { + if (codeIndentSourceBase !== null && codeIndentTargetBase !== null) { + line = adjustCodeIndent(line, codeIndentSourceBase, codeIndentTargetBase); + } out.push(line); continue; } From 7d76f2acc97140b8b63b34a3076cac6dfa17e1c1 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 12:48:07 -0700 Subject: [PATCH 14/20] fix multi-tag detection and tag followed by punctuation handling --- src/formats/bear-bear2bk.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 0dfb342e..0fdeede2 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -27,10 +27,10 @@ export class Bear2bkImporter extends FormatImporter { private static readonly nonAlphaStart = /^[0-9_\-]/; private static readonly invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; private static readonly hexColorTag = /^[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/; - private static readonly hexColorInline = /(? { - const normalizedTag = this.normalizeSimpleTag(rawTag); + const splitTag = this.splitTrailingPunctuation(rawTag); + const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); if (!normalizedTag) { return match; } - return '#' + normalizedTag; + return '#' + normalizedTag + (splitTag ? splitTag.trailing : ''); }); }); From 8d740824c7879aaa3c79ace8b4857beae39f2a1f Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 12:57:57 -0700 Subject: [PATCH 15/20] single-pass rewrite of transformOutsideCodeBlocks --- src/formats/bear-bear2bk.ts | 48 +++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 0fdeede2..22a67748 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -119,28 +119,52 @@ export class Bear2bkImporter extends FormatImporter { } private transformOutsideCodeBlocks(content: string, transformLine: (line: string) => string): string { - const hasTrailingNewline = content.endsWith('\n'); - const lines = hasTrailingNewline ? content.slice(0, -1).split('\n') : content.split('\n'); - let inCode = false; const out: string[] = []; + let inCode = false; + let lineStart = 0; + const length = content.length; + + const isCodeFenceLine = (lineValue: string): boolean => { + let idx = 0; + while (idx < lineValue.length) { + const code = lineValue.charCodeAt(idx); + if (code !== 32 && code !== 9) { + break; + } + idx += 1; + } + return lineValue.startsWith('```', idx); + }; - for (const line of lines) { - const trimmed = line.trimStart(); - if (trimmed.startsWith('```')) { - inCode = !inCode; - out.push(line); + for (let i = 0; i <= length; i += 1) { + const atLineEnd = i === length || content.charCodeAt(i) === 10; + if (!atLineEnd) { continue; } - if (inCode) { + if (i === length && lineStart === length) { + break; + } + + const line = content.slice(lineStart, i); + if (isCodeFenceLine(line)) { + inCode = !inCode; out.push(line); - continue; + } + else if (inCode) { + out.push(line); + } + else { + out.push(this.transformOutsideInlineCode(line, transformLine)); } - out.push(this.transformOutsideInlineCode(line, transformLine)); + if (i < length) { + out.push('\n'); + } + lineStart = i + 1; } - return out.join('\n') + (hasTrailingNewline ? '\n' : ''); + return out.join(''); } private transformOutsideInlineCode(line: string, transformLine: (line: string) => string): string { From 255299c164e469fdfcd1fb23b67a9e161f36867a Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 13:03:35 -0700 Subject: [PATCH 16/20] single-pass rewrite to normalize tags and escape hex colors --- src/formats/bear-bear2bk.ts | 42 +++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 22a67748..03d18cc7 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -27,10 +27,8 @@ export class Bear2bkImporter extends FormatImporter { private static readonly nonAlphaStart = /^[0-9_\-]/; private static readonly invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; private static readonly hexColorTag = /^[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/; - private static readonly hexColorInline = /(? { - const hexEscaped = line.replace(Bear2bkImporter.hexColorInline, (_match, hex) => { - return `\\#${hex}`; - }); - const enclosedNormalized = hexEscaped.replace(Bear2bkImporter.enclosedTagWithFollowingChar, (match, tag) => { - if (this.isHexColorTag(tag)) { - return match; + return line.replace(tagMatcher, (match, hexTag, enclosedFollowing, enclosedTag, rawTag) => { + if (hexTag) { + return `\\#${hexTag}`; } - return this.normalizeEnclosedTag(tag, match, true); - }).replace(Bear2bkImporter.enclosedTag, (match, tag) => { - if (this.isHexColorTag(tag)) { - return match; + if (enclosedFollowing) { + return this.normalizeEnclosedTag(enclosedFollowing, match, true); } - return this.normalizeEnclosedTag(tag, match, false); - }); - - return enclosedNormalized.replace(Bear2bkImporter.tagCandidate, (match, rawTag) => { - const splitTag = this.splitTrailingPunctuation(rawTag); - const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); - if (!normalizedTag) { - return match; + if (enclosedTag) { + return this.normalizeEnclosedTag(enclosedTag, match, false); + } + if (rawTag) { + const splitTag = this.splitTrailingPunctuation(rawTag); + const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); + if (!normalizedTag) { + return match; + } + return '#' + normalizedTag + (splitTag ? splitTag.trailing : ''); } - return '#' + normalizedTag + (splitTag ? splitTag.trailing : ''); + return match; }); }); From 2bb7aa0119d5fc64b2ebad6fff0707df1cb2d7e4 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 13:07:27 -0700 Subject: [PATCH 17/20] Updated transformOutsideInlineCode to use array accumulation and slicing instead of incremental string concatenation. --- src/formats/bear-bear2bk.ts | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 03d18cc7..869417cd 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -167,32 +167,31 @@ export class Bear2bkImporter extends FormatImporter { private transformOutsideInlineCode(line: string, transformLine: (line: string) => string): string { let inInline = false; - let current = ''; - let result = ''; + let currentStart = 0; + const parts: string[] = []; for (let i = 0; i < line.length; i += 1) { - const ch = line[i]; - if (ch === '`') { - if (!inInline) { - result += transformLine(current); - current = ''; - inInline = true; - } - else { - result += '`' + current + '`'; - current = ''; - inInline = false; - } + if (line[i] !== '`') { continue; } - current += ch; + + if (!inInline) { + parts.push(transformLine(line.slice(currentStart, i))); + inInline = true; + } + else { + parts.push('`' + line.slice(currentStart, i) + '`'); + inInline = false; + } + currentStart = i + 1; } if (inInline) { return line; } - return result + transformLine(current); + parts.push(transformLine(line.slice(currentStart))); + return parts.join(''); } private extractTagsFromContent(content: string): string[] { From aed76defa5550a7b2275c55be122df6b7c2cf610 Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 13:12:53 -0700 Subject: [PATCH 18/20] Reduced trim/trimStart usage by adding lightweight whitespace checks in fixListIndentation (frontmatter fence and code-fence detection) and removing the redundant trim() in tag extraction. --- src/formats/bear-bear2bk.ts | 38 ++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 869417cd..5946ec30 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -203,7 +203,7 @@ export class Bear2bkImporter extends FormatImporter { let match; tagCandidateRegex.lastIndex = 0; while ((match = tagCandidateRegex.exec(line)) !== null) { - const rawTag = match[1].trim(); + const rawTag = match[1]; const splitTag = this.splitTrailingPunctuation(rawTag); const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); if (!normalizedTag) { @@ -526,6 +526,34 @@ export class Bear2bkImporter extends FormatImporter { return count; }; + const countLeadingWhitespace = (value: string): number => { + let count = 0; + while (count < value.length) { + const code = value.charCodeAt(count); + if (code !== 32 && code !== 9 && code !== 13) { + break; + } + count += 1; + } + return count; + }; + + const isFrontmatterFence = (lineValue: string): boolean => { + const start = countLeadingWhitespace(lineValue); + if (!lineValue.startsWith('---', start)) { + return false; + } + let idx = start + 3; + while (idx < lineValue.length) { + const code = lineValue.charCodeAt(idx); + if (code !== 32 && code !== 9 && code !== 13) { + return false; + } + idx += 1; + } + return true; + }; + const adjustCodeIndent = (lineValue: string, sourceBase: number, targetBase: number): string => { const leadingSpaces = countLeadingSpaces(lineValue); if (leadingSpaces < sourceBase) { @@ -546,7 +574,7 @@ export class Bear2bkImporter extends FormatImporter { for (let i = 0; i < lines.length; i += 1) { let line = lines[i]; - if (i === 0 && line.trim() === '---') { + if (i === 0 && isFrontmatterFence(line)) { inFrontmatter = true; out.push(line); continue; @@ -554,15 +582,15 @@ export class Bear2bkImporter extends FormatImporter { if (inFrontmatter) { out.push(line); - if (line.trim() === '---') { + if (isFrontmatterFence(line)) { inFrontmatter = false; } continue; } const listFence = listFenceMatch(line); - const stripped = line.trimStart(); - if (listFence || stripped.startsWith('```')) { + const hasFence = listFence !== null || line.startsWith('```', countLeadingWhitespace(line)); + if (hasFence) { if (listFence) { const targetIndent = listFence.indent * 2; line = ' '.repeat(targetIndent) + listFence.marker + listFence.fence; From 08eadd9872a26cac3854502dc3bd7302d340e67e Mon Sep 17 00:00:00 2001 From: Randy Date: Fri, 27 Feb 2026 13:33:35 -0700 Subject: [PATCH 19/20] Fix tag normalization for escaped hashes and trailing invalid chars --- src/formats/bear-bear2bk.ts | 123 ++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 67 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index 5946ec30..f411f16e 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -26,9 +26,9 @@ export class Bear2bkImporter extends FormatImporter { private static readonly alphaStart = /^[A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ]/; private static readonly nonAlphaStart = /^[0-9_\-]/; private static readonly invalidChar = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/; + private static readonly invalidCharGlobal = /[^A-Za-zÀ-ÖØ-öø-įĴ-őŔ-žǍ-ǰǴ-ǵǸ-țȞ-ȟȤ-ȳɃɆ-ɏḀ-ẞƀ-ƓƗ-ƚƝ-ơƤ-ƥƫ-ưƲ-ƶẠ-ỿ0-9_\/-]/g; private static readonly hexColorTag = /^[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/; - private static readonly tagCandidate = /(?, normalizedTag: string): void { + if (this.flattenTags && normalizedTag.includes('/')) { + const parts = normalizedTag.split('/'); + for (const part of parts) { + if (part !== '' && !Bear2bkImporter.numericOnly.test(part)) { + tags.add(part); + } + } + } + else if (!Bear2bkImporter.numericOnly.test(normalizedTag)) { + tags.add(normalizedTag); + } + } + + private normalizeTagsAndCollect(content: string): { content: string; tags: string[] } { const tags = new Set(); - const isNumericOnly = (value: string) => Bear2bkImporter.numericOnly.test(value); - const tagCandidateRegex = Bear2bkImporter.tagCandidate; - - this.transformOutsideCodeBlocks(content, (line) => { - let match; - tagCandidateRegex.lastIndex = 0; - while ((match = tagCandidateRegex.exec(line)) !== null) { - const rawTag = match[1]; - const splitTag = this.splitTrailingPunctuation(rawTag); - const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); - if (!normalizedTag) { - continue; + const tagMatcher = Bear2bkImporter.tagNormalizationMatcher; + + const normalizedContent = this.transformOutsideCodeBlocks(content, (line) => { + return line.replace(tagMatcher, (match, hexTag, enclosedFollowing, enclosedTag, rawTag) => { + if (hexTag) { + return `\\#${hexTag}`; } - if (this.flattenTags && normalizedTag.includes('/')) { - const parts = normalizedTag.split('/'); - for (const part of parts) { - if (part !== '' && !isNumericOnly(part)) { - tags.add(part); - } + if (enclosedFollowing || enclosedTag) { + const enclosedValue = enclosedFollowing ?? enclosedTag; + if (/\s#$/.test(match)) { + return match; } + const normalizedSingle = enclosedValue.replace(/\s+/g, '_'); + const normalizedTag = this.normalizeSimpleTag(normalizedSingle); + if (!normalizedTag) { + return match; + } + this.addNormalizedTag(tags, normalizedTag); + return '#' + normalizedTag + (enclosedFollowing ? ' ' : ''); } - else if (!isNumericOnly(normalizedTag)) { - tags.add(normalizedTag); + + if (rawTag) { + const splitTag = this.splitTrailingPunctuation(rawTag); + const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); + if (!normalizedTag) { + return match; + } + this.addNormalizedTag(tags, normalizedTag); + return '#' + normalizedTag + (splitTag ? splitTag.trailing : ''); } - } - return line; + + return match; + }); }); - return Array.from(tags); + return { content: normalizedContent, tags: Array.from(tags) }; } async import(ctx: ImportContext): Promise { @@ -291,33 +303,10 @@ export class Bear2bkImporter extends FormatImporter { } } - // Normalize tags and escape hex color tags (single pass) - const tagMatcher = Bear2bkImporter.tagNormalizationMatcher; - mdContent = this.transformOutsideCodeBlocks(mdContent, (line) => { - return line.replace(tagMatcher, (match, hexTag, enclosedFollowing, enclosedTag, rawTag) => { - if (hexTag) { - return `\\#${hexTag}`; - } - if (enclosedFollowing) { - return this.normalizeEnclosedTag(enclosedFollowing, match, true); - } - if (enclosedTag) { - return this.normalizeEnclosedTag(enclosedTag, match, false); - } - if (rawTag) { - const splitTag = this.splitTrailingPunctuation(rawTag); - const normalizedTag = this.normalizeSimpleTag(splitTag ? splitTag.tag : rawTag); - if (!normalizedTag) { - return match; - } - return '#' + normalizedTag + (splitTag ? splitTag.trailing : ''); - } - return match; - }); - }); - - // Extract tags from content - const tags = this.extractTagsFromContent(mdContent); + // Normalize tags and collect tag list (single pass) + const normalized = this.normalizeTagsAndCollect(mdContent); + mdContent = normalized.content; + const tags = normalized.tags; // Use just the filename without extension const fileName = mdFilename; From 4d3f23b97c31117241bf68d134e0916dec5eff0a Mon Sep 17 00:00:00 2001 From: Randy Date: Mon, 2 Mar 2026 06:55:25 -0700 Subject: [PATCH 20/20] fix semi-colon usage in member delimiter --- src/formats/bear-bear2bk.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/formats/bear-bear2bk.ts b/src/formats/bear-bear2bk.ts index f411f16e..9aed5fb6 100644 --- a/src/formats/bear-bear2bk.ts +++ b/src/formats/bear-bear2bk.ts @@ -92,7 +92,7 @@ export class Bear2bkImporter extends FormatImporter { return null; } - private splitTrailingPunctuation(rawTag: string): { tag: string; trailing: string } | null { + private splitTrailingPunctuation(rawTag: string): { tag: string, trailing: string } | null { const match = rawTag.match(/^(.*?)([.,]+)$/); if (!match) { return null; @@ -199,7 +199,7 @@ export class Bear2bkImporter extends FormatImporter { } } - private normalizeTagsAndCollect(content: string): { content: string; tags: string[] } { + private normalizeTagsAndCollect(content: string): { content: string, tags: string[] } { const tags = new Set(); const tagMatcher = Bear2bkImporter.tagNormalizationMatcher; @@ -552,7 +552,7 @@ export class Bear2bkImporter extends FormatImporter { return newIndent + lineValue.slice(leadingSpaces); }; - const listFenceMatch = (lineValue: string): { indent: number; marker: string; fence: string } | null => { + const listFenceMatch = (lineValue: string): { indent: number, marker: string, fence: string } | null => { const match = lineValue.match(/^(\s*)([-*+]\s|\d+\.\s)(```.*)$/); if (!match) { return null;