@real1ty-obsidian-plugins/utils 2.14.0 → 2.16.0
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/components/frontmatter-propagation-modal.d.ts +17 -0
- package/dist/components/frontmatter-propagation-modal.d.ts.map +1 -0
- package/dist/components/frontmatter-propagation-modal.js +85 -0
- package/dist/components/frontmatter-propagation-modal.js.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/indexer.d.ts +118 -0
- package/dist/core/indexer.d.ts.map +1 -0
- package/dist/core/indexer.js +205 -0
- package/dist/core/indexer.js.map +1 -0
- package/dist/file/frontmatter-diff.d.ts +38 -0
- package/dist/file/frontmatter-diff.d.ts.map +1 -0
- package/dist/file/frontmatter-diff.js +162 -0
- package/dist/file/frontmatter-diff.js.map +1 -0
- package/dist/file/frontmatter-propagation.d.ts +12 -0
- package/dist/file/frontmatter-propagation.d.ts.map +1 -0
- package/dist/file/frontmatter-propagation.js +42 -0
- package/dist/file/frontmatter-propagation.js.map +1 -0
- package/dist/file/index.d.ts +2 -0
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +2 -0
- package/dist/file/index.js.map +1 -1
- package/dist/file/templater.d.ts +11 -1
- package/dist/file/templater.d.ts.map +1 -1
- package/dist/file/templater.js +32 -1
- package/dist/file/templater.js.map +1 -1
- package/package.json +1 -1
- package/src/components/frontmatter-propagation-modal.ts +115 -0
- package/src/components/index.ts +1 -0
- package/src/core/index.ts +1 -0
- package/src/core/indexer.ts +353 -0
- package/src/file/frontmatter-diff.ts +198 -0
- package/src/file/frontmatter-propagation.ts +59 -0
- package/src/file/index.ts +2 -0
- package/src/file/templater.ts +57 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compares two frontmatter objects and returns a detailed diff.
|
|
3
|
+
* Excludes specified properties from comparison (e.g., Prisma-managed properties).
|
|
4
|
+
*
|
|
5
|
+
* @param oldFrontmatter - The original frontmatter
|
|
6
|
+
* @param newFrontmatter - The updated frontmatter
|
|
7
|
+
* @param excludeProps - Set of property keys to exclude from comparison
|
|
8
|
+
* @returns Detailed diff with categorized changes
|
|
9
|
+
*/
|
|
10
|
+
export function compareFrontmatter(oldFrontmatter, newFrontmatter, excludeProps = new Set()) {
|
|
11
|
+
const changes = [];
|
|
12
|
+
const added = [];
|
|
13
|
+
const modified = [];
|
|
14
|
+
const deleted = [];
|
|
15
|
+
const allKeys = new Set([...Object.keys(oldFrontmatter), ...Object.keys(newFrontmatter)]);
|
|
16
|
+
for (const key of allKeys) {
|
|
17
|
+
if (excludeProps.has(key)) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const oldValue = oldFrontmatter[key];
|
|
21
|
+
const newValue = newFrontmatter[key];
|
|
22
|
+
if (!(key in oldFrontmatter) && key in newFrontmatter) {
|
|
23
|
+
const change = {
|
|
24
|
+
key,
|
|
25
|
+
oldValue: undefined,
|
|
26
|
+
newValue,
|
|
27
|
+
changeType: "added",
|
|
28
|
+
};
|
|
29
|
+
changes.push(change);
|
|
30
|
+
added.push(change);
|
|
31
|
+
}
|
|
32
|
+
else if (key in oldFrontmatter && !(key in newFrontmatter)) {
|
|
33
|
+
const change = {
|
|
34
|
+
key,
|
|
35
|
+
oldValue,
|
|
36
|
+
newValue: undefined,
|
|
37
|
+
changeType: "deleted",
|
|
38
|
+
};
|
|
39
|
+
changes.push(change);
|
|
40
|
+
deleted.push(change);
|
|
41
|
+
}
|
|
42
|
+
else if (!deepEqual(oldValue, newValue)) {
|
|
43
|
+
const change = {
|
|
44
|
+
key,
|
|
45
|
+
oldValue,
|
|
46
|
+
newValue,
|
|
47
|
+
changeType: "modified",
|
|
48
|
+
};
|
|
49
|
+
changes.push(change);
|
|
50
|
+
modified.push(change);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
hasChanges: changes.length > 0,
|
|
55
|
+
changes,
|
|
56
|
+
added,
|
|
57
|
+
modified,
|
|
58
|
+
deleted,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Deep equality check for frontmatter values.
|
|
63
|
+
* Handles primitives, arrays, and objects.
|
|
64
|
+
*/
|
|
65
|
+
function deepEqual(a, b) {
|
|
66
|
+
if (a === b)
|
|
67
|
+
return true;
|
|
68
|
+
if (a === null || b === null || a === undefined || b === undefined) {
|
|
69
|
+
return a === b;
|
|
70
|
+
}
|
|
71
|
+
if (typeof a !== typeof b)
|
|
72
|
+
return false;
|
|
73
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
74
|
+
if (a.length !== b.length)
|
|
75
|
+
return false;
|
|
76
|
+
return a.every((val, idx) => deepEqual(val, b[idx]));
|
|
77
|
+
}
|
|
78
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
79
|
+
const keysA = Object.keys(a);
|
|
80
|
+
const keysB = Object.keys(b);
|
|
81
|
+
if (keysA.length !== keysB.length)
|
|
82
|
+
return false;
|
|
83
|
+
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Merges multiple frontmatter diffs into a single accumulated diff.
|
|
89
|
+
* Later diffs override earlier ones for the same key.
|
|
90
|
+
*
|
|
91
|
+
* @param diffs - Array of diffs to merge (in chronological order)
|
|
92
|
+
* @returns A single merged diff containing all accumulated changes
|
|
93
|
+
*/
|
|
94
|
+
export function mergeFrontmatterDiffs(diffs) {
|
|
95
|
+
if (diffs.length === 0) {
|
|
96
|
+
return {
|
|
97
|
+
hasChanges: false,
|
|
98
|
+
changes: [],
|
|
99
|
+
added: [],
|
|
100
|
+
modified: [],
|
|
101
|
+
deleted: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (diffs.length === 1) {
|
|
105
|
+
return diffs[0];
|
|
106
|
+
}
|
|
107
|
+
const changesByKey = new Map();
|
|
108
|
+
for (const diff of diffs) {
|
|
109
|
+
for (const change of diff.changes) {
|
|
110
|
+
const existing = changesByKey.get(change.key);
|
|
111
|
+
if (!existing) {
|
|
112
|
+
changesByKey.set(change.key, Object.assign({}, change));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
existing.newValue = change.newValue;
|
|
116
|
+
existing.changeType = change.changeType;
|
|
117
|
+
if (existing.oldValue === change.newValue) {
|
|
118
|
+
changesByKey.delete(change.key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const allChanges = Array.from(changesByKey.values());
|
|
124
|
+
const added = allChanges.filter((c) => c.changeType === "added");
|
|
125
|
+
const modified = allChanges.filter((c) => c.changeType === "modified");
|
|
126
|
+
const deleted = allChanges.filter((c) => c.changeType === "deleted");
|
|
127
|
+
return {
|
|
128
|
+
hasChanges: allChanges.length > 0,
|
|
129
|
+
changes: allChanges,
|
|
130
|
+
added,
|
|
131
|
+
modified,
|
|
132
|
+
deleted,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Formats a frontmatter change for display in a modal.
|
|
137
|
+
* Returns a human-readable string describing the change.
|
|
138
|
+
*/
|
|
139
|
+
export function formatChangeForDisplay(change) {
|
|
140
|
+
const formatValue = (value) => {
|
|
141
|
+
if (value === undefined)
|
|
142
|
+
return "(not set)";
|
|
143
|
+
if (value === null)
|
|
144
|
+
return "null";
|
|
145
|
+
if (typeof value === "string")
|
|
146
|
+
return `"${value}"`;
|
|
147
|
+
if (typeof value === "object")
|
|
148
|
+
return JSON.stringify(value);
|
|
149
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
150
|
+
return String(value);
|
|
151
|
+
return JSON.stringify(value);
|
|
152
|
+
};
|
|
153
|
+
switch (change.changeType) {
|
|
154
|
+
case "added":
|
|
155
|
+
return `+ ${change.key}: ${formatValue(change.newValue)}`;
|
|
156
|
+
case "deleted":
|
|
157
|
+
return `- ${change.key}: ${formatValue(change.oldValue)}`;
|
|
158
|
+
case "modified":
|
|
159
|
+
return `~ ${change.key}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=frontmatter-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-diff.js","sourceRoot":"","sources":["../../src/file/frontmatter-diff.ts"],"names":[],"mappings":"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","sourcesContent":["export type Frontmatter = Record<string, unknown>;\n\nexport interface FrontmatterChange {\n\tkey: string;\n\toldValue: unknown;\n\tnewValue: unknown;\n\tchangeType: \"added\" | \"modified\" | \"deleted\";\n}\n\nexport interface FrontmatterDiff {\n\thasChanges: boolean;\n\tchanges: FrontmatterChange[];\n\tadded: FrontmatterChange[];\n\tmodified: FrontmatterChange[];\n\tdeleted: FrontmatterChange[];\n}\n\n/**\n * Compares two frontmatter objects and returns a detailed diff.\n * Excludes specified properties from comparison (e.g., Prisma-managed properties).\n *\n * @param oldFrontmatter - The original frontmatter\n * @param newFrontmatter - The updated frontmatter\n * @param excludeProps - Set of property keys to exclude from comparison\n * @returns Detailed diff with categorized changes\n */\nexport function compareFrontmatter(\n\toldFrontmatter: Frontmatter,\n\tnewFrontmatter: Frontmatter,\n\texcludeProps: Set<string> = new Set()\n): FrontmatterDiff {\n\tconst changes: FrontmatterChange[] = [];\n\tconst added: FrontmatterChange[] = [];\n\tconst modified: FrontmatterChange[] = [];\n\tconst deleted: FrontmatterChange[] = [];\n\n\tconst allKeys = new Set([...Object.keys(oldFrontmatter), ...Object.keys(newFrontmatter)]);\n\n\tfor (const key of allKeys) {\n\t\tif (excludeProps.has(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst oldValue = oldFrontmatter[key];\n\t\tconst newValue = newFrontmatter[key];\n\n\t\tif (!(key in oldFrontmatter) && key in newFrontmatter) {\n\t\t\tconst change: FrontmatterChange = {\n\t\t\t\tkey,\n\t\t\t\toldValue: undefined,\n\t\t\t\tnewValue,\n\t\t\t\tchangeType: \"added\",\n\t\t\t};\n\n\t\t\tchanges.push(change);\n\t\t\tadded.push(change);\n\t\t} else if (key in oldFrontmatter && !(key in newFrontmatter)) {\n\t\t\tconst change: FrontmatterChange = {\n\t\t\t\tkey,\n\t\t\t\toldValue,\n\t\t\t\tnewValue: undefined,\n\t\t\t\tchangeType: \"deleted\",\n\t\t\t};\n\n\t\t\tchanges.push(change);\n\t\t\tdeleted.push(change);\n\t\t} else if (!deepEqual(oldValue, newValue)) {\n\t\t\tconst change: FrontmatterChange = {\n\t\t\t\tkey,\n\t\t\t\toldValue,\n\t\t\t\tnewValue,\n\t\t\t\tchangeType: \"modified\",\n\t\t\t};\n\n\t\t\tchanges.push(change);\n\t\t\tmodified.push(change);\n\t\t}\n\t}\n\n\treturn {\n\t\thasChanges: changes.length > 0,\n\t\tchanges,\n\t\tadded,\n\t\tmodified,\n\t\tdeleted,\n\t};\n}\n\n/**\n * Deep equality check for frontmatter values.\n * Handles primitives, arrays, and objects.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n\tif (a === b) return true;\n\n\tif (a === null || b === null || a === undefined || b === undefined) {\n\t\treturn a === b;\n\t}\n\n\tif (typeof a !== typeof b) return false;\n\n\tif (Array.isArray(a) && Array.isArray(b)) {\n\t\tif (a.length !== b.length) return false;\n\t\treturn a.every((val, idx) => deepEqual(val, b[idx]));\n\t}\n\n\tif (typeof a === \"object\" && typeof b === \"object\") {\n\t\tconst keysA = Object.keys(a as Record<string, unknown>);\n\t\tconst keysB = Object.keys(b as Record<string, unknown>);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\treturn keysA.every((key) =>\n\t\t\tdeepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n\t\t);\n\t}\n\n\treturn false;\n}\n\n/**\n * Merges multiple frontmatter diffs into a single accumulated diff.\n * Later diffs override earlier ones for the same key.\n *\n * @param diffs - Array of diffs to merge (in chronological order)\n * @returns A single merged diff containing all accumulated changes\n */\nexport function mergeFrontmatterDiffs(diffs: FrontmatterDiff[]): FrontmatterDiff {\n\tif (diffs.length === 0) {\n\t\treturn {\n\t\t\thasChanges: false,\n\t\t\tchanges: [],\n\t\t\tadded: [],\n\t\t\tmodified: [],\n\t\t\tdeleted: [],\n\t\t};\n\t}\n\n\tif (diffs.length === 1) {\n\t\treturn diffs[0];\n\t}\n\n\tconst changesByKey = new Map<string, FrontmatterChange>();\n\n\tfor (const diff of diffs) {\n\t\tfor (const change of diff.changes) {\n\t\t\tconst existing = changesByKey.get(change.key);\n\n\t\t\tif (!existing) {\n\t\t\t\tchangesByKey.set(change.key, { ...change });\n\t\t\t} else {\n\t\t\t\texisting.newValue = change.newValue;\n\t\t\t\texisting.changeType = change.changeType;\n\n\t\t\t\tif (existing.oldValue === change.newValue) {\n\t\t\t\t\tchangesByKey.delete(change.key);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst allChanges = Array.from(changesByKey.values());\n\tconst added = allChanges.filter((c) => c.changeType === \"added\");\n\tconst modified = allChanges.filter((c) => c.changeType === \"modified\");\n\tconst deleted = allChanges.filter((c) => c.changeType === \"deleted\");\n\n\treturn {\n\t\thasChanges: allChanges.length > 0,\n\t\tchanges: allChanges,\n\t\tadded,\n\t\tmodified,\n\t\tdeleted,\n\t};\n}\n\n/**\n * Formats a frontmatter change for display in a modal.\n * Returns a human-readable string describing the change.\n */\nexport function formatChangeForDisplay(change: FrontmatterChange): string {\n\tconst formatValue = (value: unknown): string => {\n\t\tif (value === undefined) return \"(not set)\";\n\t\tif (value === null) return \"null\";\n\t\tif (typeof value === \"string\") return `\"${value}\"`;\n\t\tif (typeof value === \"object\") return JSON.stringify(value);\n\t\tif (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n\t\treturn JSON.stringify(value);\n\t};\n\n\tswitch (change.changeType) {\n\t\tcase \"added\":\n\t\t\treturn `+ ${change.key}: ${formatValue(change.newValue)}`;\n\t\tcase \"deleted\":\n\t\t\treturn `- ${change.key}: ${formatValue(change.oldValue)}`;\n\t\tcase \"modified\":\n\t\t\treturn `~ ${change.key}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { App } from "obsidian";
|
|
2
|
+
import type { Frontmatter, FrontmatterDiff } from "./frontmatter-diff";
|
|
3
|
+
export interface NexusPropertiesSettings {
|
|
4
|
+
excludedPropagatedProps?: string;
|
|
5
|
+
parentProp: string;
|
|
6
|
+
childrenProp: string;
|
|
7
|
+
relatedProp: string;
|
|
8
|
+
zettelIdProp: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseExcludedProps(settings: NexusPropertiesSettings): Set<string>;
|
|
11
|
+
export declare function applyFrontmatterChanges(app: App, targetPath: string, sourceFrontmatter: Frontmatter, diff: FrontmatterDiff): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=frontmatter-propagation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-propagation.d.ts","sourceRoot":"","sources":["../../src/file/frontmatter-propagation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,uBAAuB;IACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,uBAAuB,GAAG,GAAG,CAAC,MAAM,CAAC,CAejF;AAED,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,WAAW,EAC9B,IAAI,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { TFile } from "obsidian";
|
|
3
|
+
export function parseExcludedProps(settings) {
|
|
4
|
+
const excludedPropsStr = settings.excludedPropagatedProps || "";
|
|
5
|
+
const userExcluded = excludedPropsStr
|
|
6
|
+
.split(",")
|
|
7
|
+
.map((prop) => prop.trim())
|
|
8
|
+
.filter((prop) => prop.length > 0);
|
|
9
|
+
const alwaysExcluded = [
|
|
10
|
+
settings.parentProp,
|
|
11
|
+
settings.childrenProp,
|
|
12
|
+
settings.relatedProp,
|
|
13
|
+
settings.zettelIdProp,
|
|
14
|
+
];
|
|
15
|
+
return new Set([...alwaysExcluded, ...userExcluded]);
|
|
16
|
+
}
|
|
17
|
+
export function applyFrontmatterChanges(app, targetPath, sourceFrontmatter, diff) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
try {
|
|
20
|
+
const file = app.vault.getAbstractFileByPath(targetPath);
|
|
21
|
+
if (!(file instanceof TFile)) {
|
|
22
|
+
console.warn(`Target file not found: ${targetPath}`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
yield app.fileManager.processFrontMatter(file, (fm) => {
|
|
26
|
+
for (const change of diff.added) {
|
|
27
|
+
fm[change.key] = sourceFrontmatter[change.key];
|
|
28
|
+
}
|
|
29
|
+
for (const change of diff.modified) {
|
|
30
|
+
fm[change.key] = sourceFrontmatter[change.key];
|
|
31
|
+
}
|
|
32
|
+
for (const change of diff.deleted) {
|
|
33
|
+
delete fm[change.key];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(`Error applying frontmatter changes to ${targetPath}:`, error);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=frontmatter-propagation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-propagation.js","sourceRoot":"","sources":["../../src/file/frontmatter-propagation.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAWjC,MAAM,UAAU,kBAAkB,CAAC,QAAiC;IACnE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,uBAAuB,IAAI,EAAE,CAAC;IAChE,MAAM,YAAY,GAAG,gBAAgB;SACnC,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG;QACtB,QAAQ,CAAC,UAAU;QACnB,QAAQ,CAAC,YAAY;QACrB,QAAQ,CAAC,WAAW;QACpB,QAAQ,CAAC,YAAY;KACrB,CAAC;IAEF,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAgB,uBAAuB,CAC5C,GAAQ,EACR,UAAkB,EAClB,iBAA8B,EAC9B,IAAqB;;QAErB,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;YACzD,IAAI,CAAC,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;gBACrD,OAAO;YACR,CAAC;YAED,MAAM,GAAG,CAAC,WAAW,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE;gBACrD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACjC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChD,CAAC;gBAED,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChD,CAAC;gBAED,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACnC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACF,CAAC,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,yCAAyC,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9E,CAAC;IACF,CAAC;CAAA","sourcesContent":["import type { App } from \"obsidian\";\nimport { TFile } from \"obsidian\";\nimport type { Frontmatter, FrontmatterDiff } from \"./frontmatter-diff\";\n\nexport interface NexusPropertiesSettings {\n\texcludedPropagatedProps?: string;\n\tparentProp: string;\n\tchildrenProp: string;\n\trelatedProp: string;\n\tzettelIdProp: string;\n}\n\nexport function parseExcludedProps(settings: NexusPropertiesSettings): Set<string> {\n\tconst excludedPropsStr = settings.excludedPropagatedProps || \"\";\n\tconst userExcluded = excludedPropsStr\n\t\t.split(\",\")\n\t\t.map((prop) => prop.trim())\n\t\t.filter((prop) => prop.length > 0);\n\n\tconst alwaysExcluded = [\n\t\tsettings.parentProp,\n\t\tsettings.childrenProp,\n\t\tsettings.relatedProp,\n\t\tsettings.zettelIdProp,\n\t];\n\n\treturn new Set([...alwaysExcluded, ...userExcluded]);\n}\n\nexport async function applyFrontmatterChanges(\n\tapp: App,\n\ttargetPath: string,\n\tsourceFrontmatter: Frontmatter,\n\tdiff: FrontmatterDiff\n): Promise<void> {\n\ttry {\n\t\tconst file = app.vault.getAbstractFileByPath(targetPath);\n\t\tif (!(file instanceof TFile)) {\n\t\t\tconsole.warn(`Target file not found: ${targetPath}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait app.fileManager.processFrontMatter(file, (fm) => {\n\t\t\tfor (const change of diff.added) {\n\t\t\t\tfm[change.key] = sourceFrontmatter[change.key];\n\t\t\t}\n\n\t\t\tfor (const change of diff.modified) {\n\t\t\t\tfm[change.key] = sourceFrontmatter[change.key];\n\t\t\t}\n\n\t\t\tfor (const change of diff.deleted) {\n\t\t\t\tdelete fm[change.key];\n\t\t\t}\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(`Error applying frontmatter changes to ${targetPath}:`, error);\n\t}\n}\n"]}
|
package/dist/file/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./file";
|
|
|
3
3
|
export * from "./file-operations";
|
|
4
4
|
export * from "./file-utils";
|
|
5
5
|
export * from "./frontmatter";
|
|
6
|
+
export * from "./frontmatter-diff";
|
|
7
|
+
export * from "./frontmatter-propagation";
|
|
6
8
|
export * from "./link-parser";
|
|
7
9
|
export * from "./property-utils";
|
|
8
10
|
export * from "./templater";
|
package/dist/file/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
|
package/dist/file/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./file";
|
|
|
3
3
|
export * from "./file-operations";
|
|
4
4
|
export * from "./file-utils";
|
|
5
5
|
export * from "./frontmatter";
|
|
6
|
+
export * from "./frontmatter-diff";
|
|
7
|
+
export * from "./frontmatter-propagation";
|
|
6
8
|
export * from "./link-parser";
|
|
7
9
|
export * from "./property-utils";
|
|
8
10
|
export * from "./templater";
|
package/dist/file/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/file/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC","sourcesContent":["export * from \"./child-reference\";\nexport * from \"./file\";\nexport * from \"./file-operations\";\nexport * from \"./file-utils\";\nexport * from \"./frontmatter\";\nexport * from \"./link-parser\";\nexport * from \"./property-utils\";\nexport * from \"./templater\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/file/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC","sourcesContent":["export * from \"./child-reference\";\nexport * from \"./file\";\nexport * from \"./file-operations\";\nexport * from \"./file-utils\";\nexport * from \"./frontmatter\";\nexport * from \"./frontmatter-diff\";\nexport * from \"./frontmatter-propagation\";\nexport * from \"./link-parser\";\nexport * from \"./property-utils\";\nexport * from \"./templater\";\n"]}
|
package/dist/file/templater.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import { type App,
|
|
1
|
+
import { type App, TFile } from "obsidian";
|
|
2
|
+
export interface FileCreationOptions {
|
|
3
|
+
title: string;
|
|
4
|
+
targetDirectory: string;
|
|
5
|
+
filename?: string;
|
|
6
|
+
content?: string;
|
|
7
|
+
frontmatter?: Record<string, unknown>;
|
|
8
|
+
templatePath?: string;
|
|
9
|
+
useTemplater?: boolean;
|
|
10
|
+
}
|
|
2
11
|
export declare function isTemplaterAvailable(app: App): boolean;
|
|
3
12
|
export declare function createFromTemplate(app: App, templatePath: string, targetFolder?: string, filename?: string, openNewNote?: boolean): Promise<TFile | null>;
|
|
13
|
+
export declare function createFileWithTemplate(app: App, options: FileCreationOptions): Promise<TFile>;
|
|
4
14
|
//# sourceMappingURL=templater.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templater.d.ts","sourceRoot":"","sources":["../../src/file/templater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAyB,KAAK,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"templater.d.ts","sourceRoot":"","sources":["../../src/file/templater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAyB,KAAK,EAAE,MAAM,UAAU,CAAC;AAelE,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAmBD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAGtD;AAED,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,UAAQ,GACjB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CA+BvB;AAED,wBAAsB,sBAAsB,CAC3C,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,mBAAmB,GAC1B,OAAO,CAAC,KAAK,CAAC,CAyChB"}
|
package/dist/file/templater.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
-
import { Notice, normalizePath } from "obsidian";
|
|
2
|
+
import { Notice, normalizePath, TFile } from "obsidian";
|
|
3
3
|
const TEMPLATER_ID = "templater-obsidian";
|
|
4
4
|
function waitForTemplater(app_1) {
|
|
5
5
|
return __awaiter(this, arguments, void 0, function* (app, timeoutMs = 8000) {
|
|
@@ -48,4 +48,35 @@ export function createFromTemplate(app_1, templatePath_1, targetFolder_1, filena
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
+
export function createFileWithTemplate(app, options) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
const { title, targetDirectory, filename, content, frontmatter, templatePath, useTemplater } = options;
|
|
54
|
+
const finalFilename = filename || title;
|
|
55
|
+
const baseName = finalFilename.replace(/\.md$/, "");
|
|
56
|
+
const filePath = normalizePath(`${targetDirectory}/${baseName}.md`);
|
|
57
|
+
const existingFile = app.vault.getAbstractFileByPath(filePath);
|
|
58
|
+
if (existingFile instanceof TFile) {
|
|
59
|
+
return existingFile;
|
|
60
|
+
}
|
|
61
|
+
if (useTemplater && templatePath && templatePath.trim() !== "" && isTemplaterAvailable(app)) {
|
|
62
|
+
const templateFile = yield createFromTemplate(app, templatePath, targetDirectory, finalFilename);
|
|
63
|
+
if (templateFile) {
|
|
64
|
+
if (frontmatter && Object.keys(frontmatter).length > 0) {
|
|
65
|
+
yield app.fileManager.processFrontMatter(templateFile, (fm) => {
|
|
66
|
+
Object.assign(fm, frontmatter);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return templateFile;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const fileContent = content || "";
|
|
73
|
+
const file = yield app.vault.create(filePath, fileContent);
|
|
74
|
+
if (frontmatter && Object.keys(frontmatter).length > 0) {
|
|
75
|
+
yield app.fileManager.processFrontMatter(file, (fm) => {
|
|
76
|
+
Object.assign(fm, frontmatter);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return file;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
51
82
|
//# sourceMappingURL=templater.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templater.js","sourceRoot":"","sources":["../../src/file/templater.ts"],"names":[],"mappings":";AAAA,OAAO,EAAY,MAAM,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"templater.js","sourceRoot":"","sources":["../../src/file/templater.ts"],"names":[],"mappings":"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","sourcesContent":["import { type App, Notice, normalizePath, TFile } from \"obsidian\";\n\nconst TEMPLATER_ID = \"templater-obsidian\";\n\ntype CreateFn = (\n\ttemplateFile: TFile,\n\tfolder?: string,\n\tfilename?: string,\n\topenNewNote?: boolean\n) => Promise<TFile | undefined>;\n\ninterface TemplaterLike {\n\tcreate_new_note_from_template: CreateFn;\n}\n\nexport interface FileCreationOptions {\n\ttitle: string;\n\ttargetDirectory: string;\n\tfilename?: string;\n\tcontent?: string;\n\tfrontmatter?: Record<string, unknown>;\n\ttemplatePath?: string;\n\tuseTemplater?: boolean;\n}\n\nasync function waitForTemplater(app: App, timeoutMs = 8000): Promise<TemplaterLike | null> {\n\tawait new Promise<void>((resolve) => app.workspace.onLayoutReady(resolve));\n\n\tconst started = Date.now();\n\twhile (Date.now() - started < timeoutMs) {\n\t\tconst plug: any = (app as any).plugins?.getPlugin?.(TEMPLATER_ID);\n\t\tconst api = plug?.templater ?? null;\n\n\t\tconst createFn: CreateFn | undefined = api?.create_new_note_from_template?.bind(api);\n\t\tif (typeof createFn === \"function\") {\n\t\t\treturn { create_new_note_from_template: createFn };\n\t\t}\n\t\tawait new Promise((r) => setTimeout(r, 150));\n\t}\n\treturn null;\n}\n\nexport function isTemplaterAvailable(app: App): boolean {\n\tconst instance = (app as any).plugins?.getPlugin?.(TEMPLATER_ID);\n\treturn !!instance;\n}\n\nexport async function createFromTemplate(\n\tapp: App,\n\ttemplatePath: string,\n\ttargetFolder?: string,\n\tfilename?: string,\n\topenNewNote = false\n): Promise<TFile | null> {\n\tconst templater = await waitForTemplater(app);\n\tif (!templater) {\n\t\tconsole.warn(\"Templater isn't ready yet (or not installed/enabled).\");\n\t\tnew Notice(\n\t\t\t\"Templater plugin is not available or enabled. Please ensure it is installed and enabled.\"\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst templateFile = app.vault.getFileByPath(normalizePath(templatePath));\n\tif (!templateFile) {\n\t\tconsole.error(`Template not found: ${templatePath}`);\n\t\tnew Notice(`Template file not found: ${templatePath}. Please ensure the template file exists.`);\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst newFile = await templater.create_new_note_from_template(\n\t\t\ttemplateFile,\n\t\t\ttargetFolder,\n\t\t\tfilename,\n\t\t\topenNewNote\n\t\t);\n\n\t\treturn newFile ?? null;\n\t} catch (error) {\n\t\tconsole.error(\"Error creating file from template:\", error);\n\t\tnew Notice(\"Error creating file from template. Please ensure the template file is valid.\");\n\t\treturn null;\n\t}\n}\n\nexport async function createFileWithTemplate(\n\tapp: App,\n\toptions: FileCreationOptions\n): Promise<TFile> {\n\tconst { title, targetDirectory, filename, content, frontmatter, templatePath, useTemplater } =\n\t\toptions;\n\n\tconst finalFilename = filename || title;\n\tconst baseName = finalFilename.replace(/\\.md$/, \"\");\n\tconst filePath = normalizePath(`${targetDirectory}/${baseName}.md`);\n\n\tconst existingFile = app.vault.getAbstractFileByPath(filePath);\n\tif (existingFile instanceof TFile) {\n\t\treturn existingFile;\n\t}\n\n\tif (useTemplater && templatePath && templatePath.trim() !== \"\" && isTemplaterAvailable(app)) {\n\t\tconst templateFile = await createFromTemplate(\n\t\t\tapp,\n\t\t\ttemplatePath,\n\t\t\ttargetDirectory,\n\t\t\tfinalFilename\n\t\t);\n\n\t\tif (templateFile) {\n\t\t\tif (frontmatter && Object.keys(frontmatter).length > 0) {\n\t\t\t\tawait app.fileManager.processFrontMatter(templateFile, (fm) => {\n\t\t\t\t\tObject.assign(fm, frontmatter);\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn templateFile;\n\t\t}\n\t}\n\n\tconst fileContent = content || \"\";\n\tconst file = await app.vault.create(filePath, fileContent);\n\n\tif (frontmatter && Object.keys(frontmatter).length > 0) {\n\t\tawait app.fileManager.processFrontMatter(file, (fm) => {\n\t\t\tObject.assign(fm, frontmatter);\n\t\t});\n\t}\n\n\treturn file;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type App, Modal } from "obsidian";
|
|
2
|
+
|
|
3
|
+
import type { FrontmatterDiff } from "../file/frontmatter-diff";
|
|
4
|
+
import { formatChangeForDisplay } from "../file/frontmatter-diff";
|
|
5
|
+
|
|
6
|
+
export interface FrontmatterPropagationModalOptions {
|
|
7
|
+
eventTitle: string;
|
|
8
|
+
diff: FrontmatterDiff;
|
|
9
|
+
instanceCount: number;
|
|
10
|
+
onConfirm: () => void | Promise<void>;
|
|
11
|
+
onCancel?: () => void | Promise<void>;
|
|
12
|
+
cssPrefix?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class FrontmatterPropagationModal extends Modal {
|
|
16
|
+
private options: FrontmatterPropagationModalOptions;
|
|
17
|
+
|
|
18
|
+
constructor(app: App, options: FrontmatterPropagationModalOptions) {
|
|
19
|
+
super(app);
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onOpen(): void {
|
|
24
|
+
const { contentEl } = this;
|
|
25
|
+
const prefix = this.options.cssPrefix ?? "frontmatter-propagation";
|
|
26
|
+
|
|
27
|
+
contentEl.empty();
|
|
28
|
+
|
|
29
|
+
contentEl.createEl("h2", { text: "Propagate frontmatter changes?" });
|
|
30
|
+
|
|
31
|
+
contentEl.createEl("p", {
|
|
32
|
+
text: `The recurring event "${this.options.eventTitle}" has frontmatter changes. Do you want to apply these changes to all ${this.options.instanceCount} physical instances?`,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const changesContainer = contentEl.createDiv({ cls: `${prefix}-frontmatter-changes` });
|
|
36
|
+
|
|
37
|
+
if (this.options.diff.added.length > 0) {
|
|
38
|
+
const addedSection = changesContainer.createDiv({
|
|
39
|
+
cls: `${prefix}-frontmatter-changes-section`,
|
|
40
|
+
});
|
|
41
|
+
addedSection.createEl("h4", { text: "Added properties:" });
|
|
42
|
+
const addedList = addedSection.createEl("ul");
|
|
43
|
+
|
|
44
|
+
for (const change of this.options.diff.added) {
|
|
45
|
+
addedList.createEl("li", {
|
|
46
|
+
text: formatChangeForDisplay(change),
|
|
47
|
+
cls: `${prefix}-change-added`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (this.options.diff.modified.length > 0) {
|
|
53
|
+
const modifiedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });
|
|
54
|
+
modifiedSection.createEl("h4", { text: "Modified properties:" });
|
|
55
|
+
const modifiedList = modifiedSection.createEl("ul");
|
|
56
|
+
|
|
57
|
+
for (const change of this.options.diff.modified) {
|
|
58
|
+
modifiedList.createEl("li", {
|
|
59
|
+
text: formatChangeForDisplay(change),
|
|
60
|
+
cls: `${prefix}-change-modified`,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.options.diff.deleted.length > 0) {
|
|
66
|
+
const deletedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });
|
|
67
|
+
deletedSection.createEl("h4", { text: "Deleted properties:" });
|
|
68
|
+
const deletedList = deletedSection.createEl("ul");
|
|
69
|
+
|
|
70
|
+
for (const change of this.options.diff.deleted) {
|
|
71
|
+
deletedList.createEl("li", {
|
|
72
|
+
text: formatChangeForDisplay(change),
|
|
73
|
+
cls: `${prefix}-change-deleted`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const buttonContainer = contentEl.createDiv({ cls: `${prefix}-modal-buttons` });
|
|
79
|
+
|
|
80
|
+
const yesButton = buttonContainer.createEl("button", {
|
|
81
|
+
text: "Yes, propagate",
|
|
82
|
+
cls: "mod-cta",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
yesButton.addEventListener("click", () => {
|
|
86
|
+
void Promise.resolve(this.options.onConfirm())
|
|
87
|
+
.then(() => {
|
|
88
|
+
this.close();
|
|
89
|
+
})
|
|
90
|
+
.catch((error) => {
|
|
91
|
+
console.error("Error in onConfirm callback:", error);
|
|
92
|
+
this.close();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const noButton = buttonContainer.createEl("button", { text: "No, skip" });
|
|
97
|
+
|
|
98
|
+
noButton.addEventListener("click", () => {
|
|
99
|
+
const result = this.options.onCancel?.();
|
|
100
|
+
|
|
101
|
+
if (result instanceof Promise) {
|
|
102
|
+
void result.catch((error) => {
|
|
103
|
+
console.error("Error in onCancel callback:", error);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.close();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onClose(): void {
|
|
112
|
+
const { contentEl } = this;
|
|
113
|
+
contentEl.empty();
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/components/index.ts
CHANGED