@im-nobsidian/core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -0
- package/dist/index.js +238 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -340,6 +340,7 @@ declare class StateDB {
|
|
|
340
340
|
updateStatus(id: string, status: SyncStatus): void;
|
|
341
341
|
updateHash(id: string, hash: string, snapshot?: Buffer | null): void;
|
|
342
342
|
setNotionLastEdited(id: string, lastEdited: string): void;
|
|
343
|
+
setNotionParentId(id: string, parentId: string): void;
|
|
343
344
|
delete(id: string): void;
|
|
344
345
|
resolveWikilink(text: string): WikilinkEntry | null;
|
|
345
346
|
resolvePageId(pageId: string): WikilinkEntry | null;
|
|
@@ -394,6 +395,12 @@ declare class NotionClient {
|
|
|
394
395
|
updatePageProperties(pageId: string, properties: Record<string, unknown>): Promise<PageObjectResponse>;
|
|
395
396
|
getPageMarkdown(pageId: string): Promise<PageMarkdownResponse>;
|
|
396
397
|
replacePageMarkdown(pageId: string, markdown: string): Promise<PageMarkdownResponse>;
|
|
398
|
+
updatePageMarkdownPartial(pageId: string, patches: Array<{
|
|
399
|
+
oldStr: string;
|
|
400
|
+
newStr: string;
|
|
401
|
+
replaceAll?: boolean;
|
|
402
|
+
}>): Promise<PageMarkdownResponse>;
|
|
403
|
+
movePage(pageId: string, newParentId: string, newParentType: "page" | "database"): Promise<PageObjectResponse>;
|
|
397
404
|
getDatabaseSchema(databaseId: string): Promise<Record<string, {
|
|
398
405
|
id: string;
|
|
399
406
|
type: string;
|
|
@@ -607,6 +614,7 @@ declare class SyncOrchestrator {
|
|
|
607
614
|
private pushCreate;
|
|
608
615
|
private pushUpdate;
|
|
609
616
|
private pushDelete;
|
|
617
|
+
private pushMove;
|
|
610
618
|
private detectRemoteChanges;
|
|
611
619
|
private pullCreate;
|
|
612
620
|
private pullUpdate;
|
|
@@ -617,6 +625,7 @@ declare class SyncOrchestrator {
|
|
|
617
625
|
private resolveParentPath;
|
|
618
626
|
private pushCreatePage;
|
|
619
627
|
private pushUpdatePage;
|
|
628
|
+
private computePatches;
|
|
620
629
|
private fetchPageMarkdown;
|
|
621
630
|
private extractParentId;
|
|
622
631
|
private cleanupInterruptedSync;
|
package/dist/index.js
CHANGED
|
@@ -371,6 +371,11 @@ var StateDB = class _StateDB {
|
|
|
371
371
|
"UPDATE sync_state SET notion_last_edited = ?, updated_at = datetime('now') WHERE id = ?"
|
|
372
372
|
).run(lastEdited, id);
|
|
373
373
|
}
|
|
374
|
+
setNotionParentId(id, parentId) {
|
|
375
|
+
this.db.prepare(
|
|
376
|
+
"UPDATE sync_state SET notion_parent_id = ?, updated_at = datetime('now') WHERE id = ?"
|
|
377
|
+
).run(parentId, id);
|
|
378
|
+
}
|
|
374
379
|
delete(id) {
|
|
375
380
|
this.db.prepare("DELETE FROM sync_state WHERE id = ?").run(id);
|
|
376
381
|
}
|
|
@@ -568,6 +573,15 @@ var PropertyMapper = class {
|
|
|
568
573
|
});
|
|
569
574
|
return { files: filesArr };
|
|
570
575
|
}
|
|
576
|
+
case "created_time":
|
|
577
|
+
case "last_edited_time":
|
|
578
|
+
case "created_by":
|
|
579
|
+
case "last_edited_by":
|
|
580
|
+
case "formula":
|
|
581
|
+
case "rollup":
|
|
582
|
+
case "unique_id":
|
|
583
|
+
case "verification":
|
|
584
|
+
return null;
|
|
571
585
|
default:
|
|
572
586
|
return { rich_text: [{ text: { content: String(value) } }] };
|
|
573
587
|
}
|
|
@@ -760,6 +774,32 @@ var NotionClient = class {
|
|
|
760
774
|
})
|
|
761
775
|
);
|
|
762
776
|
}
|
|
777
|
+
async updatePageMarkdownPartial(pageId, patches) {
|
|
778
|
+
return this.withRateLimit(
|
|
779
|
+
() => this.client.pages.updateMarkdown({
|
|
780
|
+
page_id: pageId,
|
|
781
|
+
type: "update_content",
|
|
782
|
+
update_content: {
|
|
783
|
+
content_updates: patches.map((p) => ({
|
|
784
|
+
old_str: p.oldStr,
|
|
785
|
+
new_str: p.newStr,
|
|
786
|
+
replace_all_matches: p.replaceAll ?? false
|
|
787
|
+
})),
|
|
788
|
+
allow_deleting_content: true
|
|
789
|
+
}
|
|
790
|
+
})
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
// ─── Page Move ───
|
|
794
|
+
async movePage(pageId, newParentId, newParentType) {
|
|
795
|
+
const parent = newParentType === "database" ? { database_id: newParentId } : { page_id: newParentId };
|
|
796
|
+
return this.withRateLimit(
|
|
797
|
+
() => this.client.pages.update({
|
|
798
|
+
page_id: pageId,
|
|
799
|
+
parent
|
|
800
|
+
})
|
|
801
|
+
);
|
|
802
|
+
}
|
|
763
803
|
// ─── Database / DataSource ───
|
|
764
804
|
async getDatabaseSchema(databaseId) {
|
|
765
805
|
const db = await this.withRateLimit(
|
|
@@ -1854,7 +1894,10 @@ var FrontmatterGenerator = class {
|
|
|
1854
1894
|
function normalizeProperties(props) {
|
|
1855
1895
|
const result = {};
|
|
1856
1896
|
for (const [key, value] of Object.entries(props)) {
|
|
1857
|
-
|
|
1897
|
+
const normalized = normalizeValue(value);
|
|
1898
|
+
if (normalized !== void 0) {
|
|
1899
|
+
result[key] = normalized;
|
|
1900
|
+
}
|
|
1858
1901
|
}
|
|
1859
1902
|
return result;
|
|
1860
1903
|
}
|
|
@@ -1862,6 +1905,10 @@ function normalizeValue(value) {
|
|
|
1862
1905
|
if (typeof value === "string") {
|
|
1863
1906
|
return normalizeDate(value);
|
|
1864
1907
|
}
|
|
1908
|
+
if (Array.isArray(value)) {
|
|
1909
|
+
if (value.length === 0) return void 0;
|
|
1910
|
+
return value;
|
|
1911
|
+
}
|
|
1865
1912
|
if (typeof value === "object" && value !== null && "start" in value) {
|
|
1866
1913
|
const dateObj = value;
|
|
1867
1914
|
const normalized = {
|
|
@@ -1872,12 +1919,10 @@ function normalizeValue(value) {
|
|
|
1872
1919
|
}
|
|
1873
1920
|
return value;
|
|
1874
1921
|
}
|
|
1922
|
+
var MIDNIGHT_RE = /T00:00:00(?:\.\d{3})?(?:Z|[+-]\d{2}:\d{2})/;
|
|
1875
1923
|
function normalizeDate(dateStr) {
|
|
1876
1924
|
if (typeof dateStr !== "string") return dateStr;
|
|
1877
|
-
if (
|
|
1878
|
-
return dateStr.split("T")[0] ?? dateStr;
|
|
1879
|
-
}
|
|
1880
|
-
if (dateStr.match(/T00:00:00[Z+]/)) {
|
|
1925
|
+
if (MIDNIGHT_RE.test(dateStr)) {
|
|
1881
1926
|
return dateStr.split("T")[0] ?? dateStr;
|
|
1882
1927
|
}
|
|
1883
1928
|
return dateStr;
|
|
@@ -2855,7 +2900,13 @@ var NOTION_TOGGLE_RE = /<details>\s*<summary>([\s\S]*?)<\/summary>([\s\S]*?)<\/d
|
|
|
2855
2900
|
var NOTION_PAGE_MENTION_RE = /<mention-page id="([^"]+)">([\s\S]*?)<\/mention-page>/g;
|
|
2856
2901
|
var NOTION_USER_MENTION_RE = /<mention-user id="[^"]*">([^<]*)<\/mention-user>/g;
|
|
2857
2902
|
var NOTION_DATE_MENTION_RE = /<mention-date start="([^"]*)"(?: end="([^"]*)")?[^>]*\/>/g;
|
|
2858
|
-
var NOTION_UNKNOWN_RE = /<unknown id="[^"]*"[^>]
|
|
2903
|
+
var NOTION_UNKNOWN_RE = /<unknown id="([^"]*)"([^>]*)\/>/g;
|
|
2904
|
+
var NOTION_AUDIO_RE = /<audio src="([^"]*)">([\s\S]*?)<\/audio>/g;
|
|
2905
|
+
var NOTION_VIDEO_RE = /<video src="([^"]*)">([\s\S]*?)<\/video>/g;
|
|
2906
|
+
var NOTION_PDF_RE = /<pdf src="([^"]*)">([\s\S]*?)<\/pdf>/g;
|
|
2907
|
+
var NOTION_FILE_RE = /<file src="([^"]*)">([\s\S]*?)<\/file>/g;
|
|
2908
|
+
var NOTION_TAB_RE = /<tab title="([^"]*)">([\s\S]*?)<\/tab>/g;
|
|
2909
|
+
var NOTION_UNDERLINE_RE = /<span underline="true">([\s\S]*?)<\/span>/g;
|
|
2859
2910
|
var TOGGLE_START2 = "%%im-nobsidian:toggle:start%%";
|
|
2860
2911
|
var TOGGLE_END2 = "%%im-nobsidian:toggle:end%%";
|
|
2861
2912
|
function notionEnhancedToObsidian(enhanced) {
|
|
@@ -2866,10 +2917,13 @@ function notionEnhancedToObsidian(enhanced) {
|
|
|
2866
2917
|
result = convertPageLinks(result);
|
|
2867
2918
|
result = convertUserMentions(result);
|
|
2868
2919
|
result = convertDateMentions(result);
|
|
2869
|
-
result =
|
|
2920
|
+
result = convertMediaTags(result);
|
|
2921
|
+
result = convertTabBlocks(result);
|
|
2922
|
+
result = preserveUnknownBlocks(result);
|
|
2870
2923
|
result = convertNotionMath(result);
|
|
2871
2924
|
result = convertNotionTables(result);
|
|
2872
2925
|
result = convertColorSpans(result);
|
|
2926
|
+
result = convertUnderlineSpans(result);
|
|
2873
2927
|
result = removeEmptyBlocks(result);
|
|
2874
2928
|
result = unescapePipes(result);
|
|
2875
2929
|
return result;
|
|
@@ -2877,7 +2931,12 @@ function notionEnhancedToObsidian(enhanced) {
|
|
|
2877
2931
|
function obsidianToNotionEnhanced(obsidian) {
|
|
2878
2932
|
let result = obsidian;
|
|
2879
2933
|
result = convertTogglesToHtml(result);
|
|
2934
|
+
result = restoreTabBlocks(result);
|
|
2880
2935
|
result = convertObsidianCallouts(result);
|
|
2936
|
+
result = restoreMediaTags(result);
|
|
2937
|
+
result = restoreUnknownBlocks(result);
|
|
2938
|
+
result = restoreColorSpans(result);
|
|
2939
|
+
result = restoreUnderlineSpans(result);
|
|
2881
2940
|
return result;
|
|
2882
2941
|
}
|
|
2883
2942
|
function convertToggles(content) {
|
|
@@ -2922,8 +2981,46 @@ function convertDateMentions(content) {
|
|
|
2922
2981
|
return end ? `${start} \u2192 ${end}` : start;
|
|
2923
2982
|
});
|
|
2924
2983
|
}
|
|
2925
|
-
function
|
|
2926
|
-
return content.replace(NOTION_UNKNOWN_RE,
|
|
2984
|
+
function preserveUnknownBlocks(content) {
|
|
2985
|
+
return content.replace(NOTION_UNKNOWN_RE, (_match, id, attrs) => {
|
|
2986
|
+
const typeMatch = /type="([^"]*)"/.exec(attrs);
|
|
2987
|
+
const altMatch = /alt="([^"]*)"/.exec(attrs);
|
|
2988
|
+
const blockType = altMatch?.[1] ?? typeMatch?.[1] ?? "unknown";
|
|
2989
|
+
return `%%im-nobsidian:unknown:id=${id}&type=${blockType}%%`;
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
function convertMediaTags(content) {
|
|
2993
|
+
let result = content;
|
|
2994
|
+
result = result.replace(NOTION_AUDIO_RE, (_match, src, caption) => {
|
|
2995
|
+
const cap = caption.trim();
|
|
2996
|
+
return cap ? `[\u{1F50A} ${cap}](${src})` : `[\u{1F50A} audio](${src})`;
|
|
2997
|
+
});
|
|
2998
|
+
result = result.replace(NOTION_VIDEO_RE, (_match, src, caption) => {
|
|
2999
|
+
const cap = caption.trim();
|
|
3000
|
+
return cap ? `[\u{1F3AC} ${cap}](${src})` : `[\u{1F3AC} video](${src})`;
|
|
3001
|
+
});
|
|
3002
|
+
result = result.replace(NOTION_PDF_RE, (_match, src, caption) => {
|
|
3003
|
+
const cap = caption.trim();
|
|
3004
|
+
return cap ? `[\u{1F4C4} ${cap}](${src})` : `[\u{1F4C4} pdf](${src})`;
|
|
3005
|
+
});
|
|
3006
|
+
result = result.replace(NOTION_FILE_RE, (_match, src, caption) => {
|
|
3007
|
+
const cap = caption.trim();
|
|
3008
|
+
return cap ? `[\u{1F4CE} ${cap}](${src})` : `[\u{1F4CE} file](${src})`;
|
|
3009
|
+
});
|
|
3010
|
+
return result;
|
|
3011
|
+
}
|
|
3012
|
+
function convertTabBlocks(content) {
|
|
3013
|
+
return content.replace(NOTION_TAB_RE, (_match, title, body) => {
|
|
3014
|
+
const trimmed = body.trim();
|
|
3015
|
+
const indented = trimmed.split("\n").map((line) => `> ${line}`).join("\n");
|
|
3016
|
+
return `> [!tab] ${title}
|
|
3017
|
+
${indented}`;
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
function convertUnderlineSpans(content) {
|
|
3021
|
+
return content.replace(NOTION_UNDERLINE_RE, (_match, text) => {
|
|
3022
|
+
return `%%im-nobsidian:underline%%${text}%%/underline%%`;
|
|
3023
|
+
});
|
|
2927
3024
|
}
|
|
2928
3025
|
function convertTogglesToHtml(content) {
|
|
2929
3026
|
const startRe = new RegExp(escapeRegex3(TOGGLE_START2), "g");
|
|
@@ -3033,7 +3130,10 @@ function convertPageLinks(content) {
|
|
|
3033
3130
|
});
|
|
3034
3131
|
}
|
|
3035
3132
|
function convertColorSpans(content) {
|
|
3036
|
-
return content.replace(
|
|
3133
|
+
return content.replace(
|
|
3134
|
+
NOTION_COLOR_SPAN_RE,
|
|
3135
|
+
(_match, color, text) => `%%im-nobsidian:color:${color}%%${text}%%/color%%`
|
|
3136
|
+
);
|
|
3037
3137
|
}
|
|
3038
3138
|
function removeEmptyBlocks(content) {
|
|
3039
3139
|
return content.replace(NOTION_EMPTY_BLOCK_RE, "\n");
|
|
@@ -3080,6 +3180,51 @@ function convertNotionTables(content) {
|
|
|
3080
3180
|
return lines.join("\n");
|
|
3081
3181
|
});
|
|
3082
3182
|
}
|
|
3183
|
+
var OBSIDIAN_MEDIA_AUDIO_RE = /\[🔊\s*([^\]]*)\]\(([^)]+)\)/g;
|
|
3184
|
+
var OBSIDIAN_MEDIA_VIDEO_RE = /\[🎬\s*([^\]]*)\]\(([^)]+)\)/g;
|
|
3185
|
+
var OBSIDIAN_MEDIA_PDF_RE = /\[📄\s*([^\]]*)\]\(([^)]+)\)/g;
|
|
3186
|
+
var OBSIDIAN_MEDIA_FILE_RE = /\[📎\s*([^\]]*)\]\(([^)]+)\)/g;
|
|
3187
|
+
function restoreMediaTags(content) {
|
|
3188
|
+
let result = content;
|
|
3189
|
+
result = result.replace(OBSIDIAN_MEDIA_AUDIO_RE, (_match, caption, src) => {
|
|
3190
|
+
return `<audio src="${src}">${caption}</audio>`;
|
|
3191
|
+
});
|
|
3192
|
+
result = result.replace(OBSIDIAN_MEDIA_VIDEO_RE, (_match, caption, src) => {
|
|
3193
|
+
return `<video src="${src}">${caption}</video>`;
|
|
3194
|
+
});
|
|
3195
|
+
result = result.replace(OBSIDIAN_MEDIA_PDF_RE, (_match, caption, src) => {
|
|
3196
|
+
return `<pdf src="${src}">${caption}</pdf>`;
|
|
3197
|
+
});
|
|
3198
|
+
result = result.replace(OBSIDIAN_MEDIA_FILE_RE, (_match, caption, src) => {
|
|
3199
|
+
return `<file src="${src}">${caption}</file>`;
|
|
3200
|
+
});
|
|
3201
|
+
return result;
|
|
3202
|
+
}
|
|
3203
|
+
var OBSIDIAN_TAB_RE = /^> \[!tab\]\s*(.+)\n((?:> .*\n?)*)/gm;
|
|
3204
|
+
function restoreTabBlocks(content) {
|
|
3205
|
+
return content.replace(OBSIDIAN_TAB_RE, (_match, title, body) => {
|
|
3206
|
+
const unquoted = body.split("\n").map((line) => line.replace(/^> /, "")).join("\n").trim();
|
|
3207
|
+
return `<tab title="${title}">${unquoted}</tab>`;
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
var OBSIDIAN_UNKNOWN_RE = /%%im-nobsidian:unknown:id=([^&]+)&type=([^%]+)%%/g;
|
|
3211
|
+
function restoreUnknownBlocks(content) {
|
|
3212
|
+
return content.replace(OBSIDIAN_UNKNOWN_RE, (_match, id, type) => {
|
|
3213
|
+
return `<unknown id="${id}" type="${type}"/>`;
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
var OBSIDIAN_COLOR_RE = /%%im-nobsidian:color:([^%]+)%%([\s\S]*?)%%\/color%%/g;
|
|
3217
|
+
function restoreColorSpans(content) {
|
|
3218
|
+
return content.replace(OBSIDIAN_COLOR_RE, (_match, color, text) => {
|
|
3219
|
+
return `<span color="${color}">${text}</span>`;
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
var OBSIDIAN_UNDERLINE_RE = /%%im-nobsidian:underline%%([\s\S]*?)%%\/underline%%/g;
|
|
3223
|
+
function restoreUnderlineSpans(content) {
|
|
3224
|
+
return content.replace(OBSIDIAN_UNDERLINE_RE, (_match, text) => {
|
|
3225
|
+
return `<span underline="true">${text}</span>`;
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3083
3228
|
function unescapePipes(content) {
|
|
3084
3229
|
const lines = content.split("\n");
|
|
3085
3230
|
const result = [];
|
|
@@ -3189,10 +3334,13 @@ var SyncOrchestrator = class {
|
|
|
3189
3334
|
created++;
|
|
3190
3335
|
break;
|
|
3191
3336
|
case "modified":
|
|
3192
|
-
case "moved":
|
|
3193
3337
|
await this.pushUpdate(change.path);
|
|
3194
3338
|
updated++;
|
|
3195
3339
|
break;
|
|
3340
|
+
case "moved":
|
|
3341
|
+
await this.pushMove(change.path);
|
|
3342
|
+
updated++;
|
|
3343
|
+
break;
|
|
3196
3344
|
case "deleted":
|
|
3197
3345
|
await this.pushDelete(change.path);
|
|
3198
3346
|
deleted++;
|
|
@@ -3369,13 +3517,20 @@ var SyncOrchestrator = class {
|
|
|
3369
3517
|
localContent = await this.vaultFs.readFile(record.obsidianPath);
|
|
3370
3518
|
} catch {
|
|
3371
3519
|
}
|
|
3520
|
+
let remoteContent = "";
|
|
3521
|
+
if (record.notionPageId) {
|
|
3522
|
+
try {
|
|
3523
|
+
remoteContent = await this.fetchPageMarkdown(record.notionPageId);
|
|
3524
|
+
} catch {
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3372
3527
|
conflicts.push({
|
|
3373
3528
|
syncRecord: record,
|
|
3374
3529
|
localChange,
|
|
3375
3530
|
remoteChange,
|
|
3376
3531
|
baseContent: record.baseSnapshot?.toString("utf-8") ?? null,
|
|
3377
3532
|
localContent,
|
|
3378
|
-
remoteContent
|
|
3533
|
+
remoteContent
|
|
3379
3534
|
});
|
|
3380
3535
|
}
|
|
3381
3536
|
return conflicts;
|
|
@@ -3465,7 +3620,7 @@ var SyncOrchestrator = class {
|
|
|
3465
3620
|
filePath: path,
|
|
3466
3621
|
parentMode: this.config.notion.parentMode
|
|
3467
3622
|
});
|
|
3468
|
-
await this.pushUpdatePage(record.notionPageId, conversionResult.content);
|
|
3623
|
+
await this.pushUpdatePage(record.notionPageId, conversionResult.content, record.baseSnapshot);
|
|
3469
3624
|
await this.imageHandler.uploadAndAppendImages(record.notionPageId, conversionResult.images);
|
|
3470
3625
|
let propsToUpdate = conversionResult.properties;
|
|
3471
3626
|
if (this.isDatabaseMode && propsToUpdate && Object.keys(propsToUpdate).length > 0) {
|
|
@@ -3503,6 +3658,22 @@ var SyncOrchestrator = class {
|
|
|
3503
3658
|
this.stateDb.updateStatus(record.id, "pending");
|
|
3504
3659
|
}
|
|
3505
3660
|
}
|
|
3661
|
+
async pushMove(path) {
|
|
3662
|
+
const record = this.stateDb.getByPath(path);
|
|
3663
|
+
if (!record?.notionPageId) return;
|
|
3664
|
+
const newParentId = await this.resolveNotionParent(path);
|
|
3665
|
+
const currentParentId = record.notionParentId;
|
|
3666
|
+
if (currentParentId && !notionIdsEqual(newParentId, currentParentId)) {
|
|
3667
|
+
try {
|
|
3668
|
+
const parentType = this.isDatabaseMode ? "database" : "page";
|
|
3669
|
+
await this.notionClient.movePage(record.notionPageId, newParentId, parentType);
|
|
3670
|
+
this.stateDb.setNotionParentId(record.id, newParentId);
|
|
3671
|
+
} catch {
|
|
3672
|
+
getLogger().warn(`[Im-Nobsidian] Move API \uC2E4\uD328 \u2014 "${path}" \uB0B4\uC6A9\uB9CC \uC5C5\uB370\uC774\uD2B8\uD569\uB2C8\uB2E4`);
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
await this.pushUpdate(path);
|
|
3676
|
+
}
|
|
3506
3677
|
async detectRemoteChanges() {
|
|
3507
3678
|
const changes = [];
|
|
3508
3679
|
const lastPull = this.stateDb.getMeta("last_pull_at");
|
|
@@ -3838,10 +4009,18 @@ var SyncOrchestrator = class {
|
|
|
3838
4009
|
}
|
|
3839
4010
|
return page;
|
|
3840
4011
|
}
|
|
3841
|
-
async pushUpdatePage(pageId, markdownContent) {
|
|
4012
|
+
async pushUpdatePage(pageId, markdownContent, baseSnapshot) {
|
|
3842
4013
|
if (this.config.conversion.preferMarkdownApi !== false) {
|
|
3843
4014
|
try {
|
|
3844
4015
|
const enhanced = obsidianToNotionEnhanced(markdownContent);
|
|
4016
|
+
if (baseSnapshot) {
|
|
4017
|
+
const baseEnhanced = obsidianToNotionEnhanced(baseSnapshot.toString("utf-8"));
|
|
4018
|
+
const patches = this.computePatches(baseEnhanced, enhanced);
|
|
4019
|
+
if (patches.length > 0 && patches.length <= 20) {
|
|
4020
|
+
await this.notionClient.updatePageMarkdownPartial(pageId, patches);
|
|
4021
|
+
return;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
3845
4024
|
await this.notionClient.replacePageMarkdown(pageId, enhanced);
|
|
3846
4025
|
return;
|
|
3847
4026
|
} catch {
|
|
@@ -3864,6 +4043,51 @@ var SyncOrchestrator = class {
|
|
|
3864
4043
|
})
|
|
3865
4044
|
);
|
|
3866
4045
|
}
|
|
4046
|
+
computePatches(oldContent, newContent) {
|
|
4047
|
+
const oldLines = oldContent.split("\n");
|
|
4048
|
+
const newLines = newContent.split("\n");
|
|
4049
|
+
const patches = [];
|
|
4050
|
+
let i = 0;
|
|
4051
|
+
let j = 0;
|
|
4052
|
+
while (i < oldLines.length && j < newLines.length) {
|
|
4053
|
+
if (oldLines[i] === newLines[j]) {
|
|
4054
|
+
i++;
|
|
4055
|
+
j++;
|
|
4056
|
+
continue;
|
|
4057
|
+
}
|
|
4058
|
+
let oldEnd = i + 1;
|
|
4059
|
+
let newEnd = j + 1;
|
|
4060
|
+
const lookahead = Math.min(10, oldLines.length - i, newLines.length - j);
|
|
4061
|
+
for (let k = 1; k <= lookahead; k++) {
|
|
4062
|
+
if (i + k < oldLines.length && newLines[j] === oldLines[i + k]) {
|
|
4063
|
+
oldEnd = i + k;
|
|
4064
|
+
newEnd = j;
|
|
4065
|
+
break;
|
|
4066
|
+
}
|
|
4067
|
+
if (j + k < newLines.length && oldLines[i] === newLines[j + k]) {
|
|
4068
|
+
oldEnd = i;
|
|
4069
|
+
newEnd = j + k;
|
|
4070
|
+
break;
|
|
4071
|
+
}
|
|
4072
|
+
oldEnd = i + k;
|
|
4073
|
+
newEnd = j + k;
|
|
4074
|
+
}
|
|
4075
|
+
const oldChunk = oldLines.slice(i, oldEnd).join("\n");
|
|
4076
|
+
const newChunk = newLines.slice(j, newEnd).join("\n");
|
|
4077
|
+
if (oldChunk || newChunk) {
|
|
4078
|
+
patches.push({ oldStr: oldChunk, newStr: newChunk });
|
|
4079
|
+
}
|
|
4080
|
+
i = oldEnd;
|
|
4081
|
+
j = newEnd;
|
|
4082
|
+
}
|
|
4083
|
+
if (i < oldLines.length || j < newLines.length) {
|
|
4084
|
+
patches.push({
|
|
4085
|
+
oldStr: oldLines.slice(i).join("\n"),
|
|
4086
|
+
newStr: newLines.slice(j).join("\n")
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
return patches;
|
|
4090
|
+
}
|
|
3867
4091
|
async fetchPageMarkdown(pageId) {
|
|
3868
4092
|
if (this.config.conversion.preferMarkdownApi !== false) {
|
|
3869
4093
|
try {
|