@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,17 @@
|
|
|
1
|
+
import { type App, Modal } from "obsidian";
|
|
2
|
+
import type { FrontmatterDiff } from "../file/frontmatter-diff";
|
|
3
|
+
export interface FrontmatterPropagationModalOptions {
|
|
4
|
+
eventTitle: string;
|
|
5
|
+
diff: FrontmatterDiff;
|
|
6
|
+
instanceCount: number;
|
|
7
|
+
onConfirm: () => void | Promise<void>;
|
|
8
|
+
onCancel?: () => void | Promise<void>;
|
|
9
|
+
cssPrefix?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class FrontmatterPropagationModal extends Modal {
|
|
12
|
+
private options;
|
|
13
|
+
constructor(app: App, options: FrontmatterPropagationModalOptions);
|
|
14
|
+
onOpen(): void;
|
|
15
|
+
onClose(): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=frontmatter-propagation-modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-propagation-modal.d.ts","sourceRoot":"","sources":["../../src/components/frontmatter-propagation-modal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGhE,MAAM,WAAW,kCAAkC;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,eAAe,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACrD,OAAO,CAAC,OAAO,CAAqC;gBAExC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,kCAAkC;IAKjE,MAAM,IAAI,IAAI;IAwFd,OAAO,IAAI,IAAI;CAIf"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Modal } from "obsidian";
|
|
2
|
+
import { formatChangeForDisplay } from "../file/frontmatter-diff";
|
|
3
|
+
export class FrontmatterPropagationModal extends Modal {
|
|
4
|
+
constructor(app, options) {
|
|
5
|
+
super(app);
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
onOpen() {
|
|
9
|
+
var _a;
|
|
10
|
+
const { contentEl } = this;
|
|
11
|
+
const prefix = (_a = this.options.cssPrefix) !== null && _a !== void 0 ? _a : "frontmatter-propagation";
|
|
12
|
+
contentEl.empty();
|
|
13
|
+
contentEl.createEl("h2", { text: "Propagate frontmatter changes?" });
|
|
14
|
+
contentEl.createEl("p", {
|
|
15
|
+
text: `The recurring event "${this.options.eventTitle}" has frontmatter changes. Do you want to apply these changes to all ${this.options.instanceCount} physical instances?`,
|
|
16
|
+
});
|
|
17
|
+
const changesContainer = contentEl.createDiv({ cls: `${prefix}-frontmatter-changes` });
|
|
18
|
+
if (this.options.diff.added.length > 0) {
|
|
19
|
+
const addedSection = changesContainer.createDiv({
|
|
20
|
+
cls: `${prefix}-frontmatter-changes-section`,
|
|
21
|
+
});
|
|
22
|
+
addedSection.createEl("h4", { text: "Added properties:" });
|
|
23
|
+
const addedList = addedSection.createEl("ul");
|
|
24
|
+
for (const change of this.options.diff.added) {
|
|
25
|
+
addedList.createEl("li", {
|
|
26
|
+
text: formatChangeForDisplay(change),
|
|
27
|
+
cls: `${prefix}-change-added`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (this.options.diff.modified.length > 0) {
|
|
32
|
+
const modifiedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });
|
|
33
|
+
modifiedSection.createEl("h4", { text: "Modified properties:" });
|
|
34
|
+
const modifiedList = modifiedSection.createEl("ul");
|
|
35
|
+
for (const change of this.options.diff.modified) {
|
|
36
|
+
modifiedList.createEl("li", {
|
|
37
|
+
text: formatChangeForDisplay(change),
|
|
38
|
+
cls: `${prefix}-change-modified`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (this.options.diff.deleted.length > 0) {
|
|
43
|
+
const deletedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });
|
|
44
|
+
deletedSection.createEl("h4", { text: "Deleted properties:" });
|
|
45
|
+
const deletedList = deletedSection.createEl("ul");
|
|
46
|
+
for (const change of this.options.diff.deleted) {
|
|
47
|
+
deletedList.createEl("li", {
|
|
48
|
+
text: formatChangeForDisplay(change),
|
|
49
|
+
cls: `${prefix}-change-deleted`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const buttonContainer = contentEl.createDiv({ cls: `${prefix}-modal-buttons` });
|
|
54
|
+
const yesButton = buttonContainer.createEl("button", {
|
|
55
|
+
text: "Yes, propagate",
|
|
56
|
+
cls: "mod-cta",
|
|
57
|
+
});
|
|
58
|
+
yesButton.addEventListener("click", () => {
|
|
59
|
+
void Promise.resolve(this.options.onConfirm())
|
|
60
|
+
.then(() => {
|
|
61
|
+
this.close();
|
|
62
|
+
})
|
|
63
|
+
.catch((error) => {
|
|
64
|
+
console.error("Error in onConfirm callback:", error);
|
|
65
|
+
this.close();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
const noButton = buttonContainer.createEl("button", { text: "No, skip" });
|
|
69
|
+
noButton.addEventListener("click", () => {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
const result = (_b = (_a = this.options).onCancel) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
72
|
+
if (result instanceof Promise) {
|
|
73
|
+
void result.catch((error) => {
|
|
74
|
+
console.error("Error in onCancel callback:", error);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
this.close();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
onClose() {
|
|
81
|
+
const { contentEl } = this;
|
|
82
|
+
contentEl.empty();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=frontmatter-propagation-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-propagation-modal.js","sourceRoot":"","sources":["../../src/components/frontmatter-propagation-modal.ts"],"names":[],"mappings":"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","sourcesContent":["import { type App, Modal } from \"obsidian\";\n\nimport type { FrontmatterDiff } from \"../file/frontmatter-diff\";\nimport { formatChangeForDisplay } from \"../file/frontmatter-diff\";\n\nexport interface FrontmatterPropagationModalOptions {\n\teventTitle: string;\n\tdiff: FrontmatterDiff;\n\tinstanceCount: number;\n\tonConfirm: () => void | Promise<void>;\n\tonCancel?: () => void | Promise<void>;\n\tcssPrefix?: string;\n}\n\nexport class FrontmatterPropagationModal extends Modal {\n\tprivate options: FrontmatterPropagationModalOptions;\n\n\tconstructor(app: App, options: FrontmatterPropagationModalOptions) {\n\t\tsuper(app);\n\t\tthis.options = options;\n\t}\n\n\tonOpen(): void {\n\t\tconst { contentEl } = this;\n\t\tconst prefix = this.options.cssPrefix ?? \"frontmatter-propagation\";\n\n\t\tcontentEl.empty();\n\n\t\tcontentEl.createEl(\"h2\", { text: \"Propagate frontmatter changes?\" });\n\n\t\tcontentEl.createEl(\"p\", {\n\t\t\ttext: `The recurring event \"${this.options.eventTitle}\" has frontmatter changes. Do you want to apply these changes to all ${this.options.instanceCount} physical instances?`,\n\t\t});\n\n\t\tconst changesContainer = contentEl.createDiv({ cls: `${prefix}-frontmatter-changes` });\n\n\t\tif (this.options.diff.added.length > 0) {\n\t\t\tconst addedSection = changesContainer.createDiv({\n\t\t\t\tcls: `${prefix}-frontmatter-changes-section`,\n\t\t\t});\n\t\t\taddedSection.createEl(\"h4\", { text: \"Added properties:\" });\n\t\t\tconst addedList = addedSection.createEl(\"ul\");\n\n\t\t\tfor (const change of this.options.diff.added) {\n\t\t\t\taddedList.createEl(\"li\", {\n\t\t\t\t\ttext: formatChangeForDisplay(change),\n\t\t\t\t\tcls: `${prefix}-change-added`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (this.options.diff.modified.length > 0) {\n\t\t\tconst modifiedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });\n\t\t\tmodifiedSection.createEl(\"h4\", { text: \"Modified properties:\" });\n\t\t\tconst modifiedList = modifiedSection.createEl(\"ul\");\n\n\t\t\tfor (const change of this.options.diff.modified) {\n\t\t\t\tmodifiedList.createEl(\"li\", {\n\t\t\t\t\ttext: formatChangeForDisplay(change),\n\t\t\t\t\tcls: `${prefix}-change-modified`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (this.options.diff.deleted.length > 0) {\n\t\t\tconst deletedSection = changesContainer.createDiv({ cls: `${prefix}-changes-section` });\n\t\t\tdeletedSection.createEl(\"h4\", { text: \"Deleted properties:\" });\n\t\t\tconst deletedList = deletedSection.createEl(\"ul\");\n\n\t\t\tfor (const change of this.options.diff.deleted) {\n\t\t\t\tdeletedList.createEl(\"li\", {\n\t\t\t\t\ttext: formatChangeForDisplay(change),\n\t\t\t\t\tcls: `${prefix}-change-deleted`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst buttonContainer = contentEl.createDiv({ cls: `${prefix}-modal-buttons` });\n\n\t\tconst yesButton = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Yes, propagate\",\n\t\t\tcls: \"mod-cta\",\n\t\t});\n\n\t\tyesButton.addEventListener(\"click\", () => {\n\t\t\tvoid Promise.resolve(this.options.onConfirm())\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis.close();\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tconsole.error(\"Error in onConfirm callback:\", error);\n\t\t\t\t\tthis.close();\n\t\t\t\t});\n\t\t});\n\n\t\tconst noButton = buttonContainer.createEl(\"button\", { text: \"No, skip\" });\n\n\t\tnoButton.addEventListener(\"click\", () => {\n\t\t\tconst result = this.options.onCancel?.();\n\n\t\t\tif (result instanceof Promise) {\n\t\t\t\tvoid result.catch((error) => {\n\t\t\t\t\tconsole.error(\"Error in onCancel callback:\", error);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.close();\n\t\t});\n\t}\n\n\tonClose(): void {\n\t\tconst { contentEl } = this;\n\t\tcontentEl.empty();\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,kBAAkB,CAAC"}
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC","sourcesContent":["export * from \"./input-managers\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,kBAAkB,CAAC","sourcesContent":["export * from \"./frontmatter-propagation-modal\";\nexport * from \"./input-managers\";\n"]}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./evaluator";
|
|
|
4
4
|
export * from "./expression-utils";
|
|
5
5
|
export * from "./frontmatter-value";
|
|
6
6
|
export * from "./generate";
|
|
7
|
+
export * from "./indexer";
|
|
7
8
|
export * from "./property-renderer";
|
|
8
9
|
export * from "./validation";
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./evaluator";
|
|
|
4
4
|
export * from "./expression-utils";
|
|
5
5
|
export * from "./frontmatter-value";
|
|
6
6
|
export * from "./generate";
|
|
7
|
+
export * from "./indexer";
|
|
7
8
|
export * from "./property-renderer";
|
|
8
9
|
export * from "./validation";
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./color-utils\";\nexport * from \"./css-utils\";\nexport * from \"./evaluator\";\nexport * from \"./expression-utils\";\nexport * from \"./frontmatter-value\";\nexport * from \"./generate\";\nexport * from \"./property-renderer\";\nexport * from \"./validation\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./color-utils\";\nexport * from \"./css-utils\";\nexport * from \"./evaluator\";\nexport * from \"./expression-utils\";\nexport * from \"./frontmatter-value\";\nexport * from \"./generate\";\nexport * from \"./indexer\";\nexport * from \"./property-renderer\";\nexport * from \"./validation\";\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type App } from "obsidian";
|
|
2
|
+
import { type BehaviorSubject, type Observable } from "rxjs";
|
|
3
|
+
import { type FrontmatterDiff } from "../file/frontmatter-diff";
|
|
4
|
+
/**
|
|
5
|
+
* Generic frontmatter object type for indexer
|
|
6
|
+
*/
|
|
7
|
+
export type IndexerFrontmatter = Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for the generic indexer
|
|
10
|
+
*/
|
|
11
|
+
export interface IndexerConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Directory to scan for files (e.g., "Calendar", "Notes")
|
|
14
|
+
* If empty string or undefined, scans entire vault
|
|
15
|
+
*/
|
|
16
|
+
directory?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Properties to exclude when comparing frontmatter diffs
|
|
19
|
+
*/
|
|
20
|
+
excludedDiffProps?: Set<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Concurrency limit for file scanning operations
|
|
23
|
+
*/
|
|
24
|
+
scanConcurrency?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Debounce time in milliseconds for file change events
|
|
27
|
+
*/
|
|
28
|
+
debounceMs?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Raw file source with frontmatter and metadata
|
|
32
|
+
*/
|
|
33
|
+
export interface FileSource {
|
|
34
|
+
filePath: string;
|
|
35
|
+
mtime: number;
|
|
36
|
+
frontmatter: IndexerFrontmatter;
|
|
37
|
+
folder: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Types of indexer events
|
|
41
|
+
*/
|
|
42
|
+
export type IndexerEventType = "file-changed" | "file-deleted";
|
|
43
|
+
/**
|
|
44
|
+
* Generic indexer event
|
|
45
|
+
*/
|
|
46
|
+
export interface IndexerEvent {
|
|
47
|
+
type: IndexerEventType;
|
|
48
|
+
filePath: string;
|
|
49
|
+
oldPath?: string;
|
|
50
|
+
source?: FileSource;
|
|
51
|
+
oldFrontmatter?: IndexerFrontmatter;
|
|
52
|
+
frontmatterDiff?: FrontmatterDiff;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generic indexer that listens to Obsidian vault events and emits
|
|
56
|
+
* RxJS observables with frontmatter diffs and metadata.
|
|
57
|
+
*
|
|
58
|
+
* This indexer is framework-agnostic and can be used by any plugin
|
|
59
|
+
* that needs to track file changes with frontmatter.
|
|
60
|
+
*/
|
|
61
|
+
export declare class Indexer {
|
|
62
|
+
private config;
|
|
63
|
+
private fileSub;
|
|
64
|
+
private configSubscription;
|
|
65
|
+
private vault;
|
|
66
|
+
private metadataCache;
|
|
67
|
+
private scanEventsSubject;
|
|
68
|
+
private indexingCompleteSubject;
|
|
69
|
+
private frontmatterCache;
|
|
70
|
+
readonly events$: Observable<IndexerEvent>;
|
|
71
|
+
readonly indexingComplete$: Observable<boolean>;
|
|
72
|
+
constructor(_app: App, configStore: BehaviorSubject<IndexerConfig>);
|
|
73
|
+
/**
|
|
74
|
+
* Start the indexer and perform initial scan
|
|
75
|
+
*/
|
|
76
|
+
start(): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Stop the indexer and clean up subscriptions
|
|
79
|
+
*/
|
|
80
|
+
stop(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Clear cache and rescan all files
|
|
83
|
+
*/
|
|
84
|
+
resync(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Scan all markdown files in the configured directory
|
|
87
|
+
*/
|
|
88
|
+
private scanAllFiles;
|
|
89
|
+
/**
|
|
90
|
+
* Create an observable from a vault event
|
|
91
|
+
*/
|
|
92
|
+
private fromVaultEvent;
|
|
93
|
+
/**
|
|
94
|
+
* Type guard to check if file is a markdown file
|
|
95
|
+
*/
|
|
96
|
+
private static isMarkdownFile;
|
|
97
|
+
/**
|
|
98
|
+
* Filter to only relevant markdown files in configured directory
|
|
99
|
+
*/
|
|
100
|
+
private toRelevantFiles;
|
|
101
|
+
/**
|
|
102
|
+
* Debounce events by file path
|
|
103
|
+
*/
|
|
104
|
+
private debounceByPath;
|
|
105
|
+
/**
|
|
106
|
+
* Build the file system events observable stream
|
|
107
|
+
*/
|
|
108
|
+
private buildFileSystemEvents$;
|
|
109
|
+
/**
|
|
110
|
+
* Build an indexer event from a file
|
|
111
|
+
*/
|
|
112
|
+
private buildEvent;
|
|
113
|
+
/**
|
|
114
|
+
* Check if file is in the configured directory
|
|
115
|
+
*/
|
|
116
|
+
private isRelevantFile;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=indexer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/core/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAA6D,MAAM,UAAU,CAAC;AAC/F,OAAO,EACN,KAAK,eAAe,EAKpB,KAAK,UAAU,EAKf,MAAM,MAAM,CAAC;AAGd,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAEhC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,kBAAkB,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,cAAc,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAQD;;;;;;GAMG;AACH,qBAAa,OAAO;IACnB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,uBAAuB,CAAyC;IACxE,OAAO,CAAC,gBAAgB,CAA8C;IAEtE,SAAgB,OAAO,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IAClD,SAAgB,iBAAiB,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;gBAE3C,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,CAAC,aAAa,CAAC;IAkClE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,MAAM,IAAI,IAAI;IAMd;;OAEG;YACW,YAAY;IA+B1B;;OAEG;IACH,OAAO,CAAC,cAAc;IA6BtB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAI7B;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA4C9B;;OAEG;YACW,UAAU;IA+BxB;;OAEG;IACH,OAAO,CAAC,cAAc;CAGtB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { TFile } from "obsidian";
|
|
3
|
+
import { from, fromEventPattern, lastValueFrom, merge, of, BehaviorSubject as RxBehaviorSubject, Subject, } from "rxjs";
|
|
4
|
+
import { debounceTime, filter, groupBy, map, mergeMap, switchMap, toArray } from "rxjs/operators";
|
|
5
|
+
import { isFileInConfiguredDirectory } from "../file/file";
|
|
6
|
+
import { compareFrontmatter } from "../file/frontmatter-diff";
|
|
7
|
+
/**
|
|
8
|
+
* Generic indexer that listens to Obsidian vault events and emits
|
|
9
|
+
* RxJS observables with frontmatter diffs and metadata.
|
|
10
|
+
*
|
|
11
|
+
* This indexer is framework-agnostic and can be used by any plugin
|
|
12
|
+
* that needs to track file changes with frontmatter.
|
|
13
|
+
*/
|
|
14
|
+
export class Indexer {
|
|
15
|
+
constructor(_app, configStore) {
|
|
16
|
+
this.fileSub = null;
|
|
17
|
+
this.configSubscription = null;
|
|
18
|
+
this.scanEventsSubject = new Subject();
|
|
19
|
+
this.indexingCompleteSubject = new RxBehaviorSubject(false);
|
|
20
|
+
this.frontmatterCache = new Map();
|
|
21
|
+
this.vault = _app.vault;
|
|
22
|
+
this.metadataCache = _app.metadataCache;
|
|
23
|
+
// Set defaults
|
|
24
|
+
this.config = {
|
|
25
|
+
directory: configStore.value.directory || "",
|
|
26
|
+
excludedDiffProps: configStore.value.excludedDiffProps || new Set(),
|
|
27
|
+
scanConcurrency: configStore.value.scanConcurrency || 10,
|
|
28
|
+
debounceMs: configStore.value.debounceMs || 100,
|
|
29
|
+
};
|
|
30
|
+
// Subscribe to config changes
|
|
31
|
+
this.configSubscription = configStore.subscribe((newConfig) => {
|
|
32
|
+
const directoryChanged = this.config.directory !== (newConfig.directory || "");
|
|
33
|
+
this.config = {
|
|
34
|
+
directory: newConfig.directory || "",
|
|
35
|
+
excludedDiffProps: newConfig.excludedDiffProps || new Set(),
|
|
36
|
+
scanConcurrency: newConfig.scanConcurrency || 10,
|
|
37
|
+
debounceMs: newConfig.debounceMs || 100,
|
|
38
|
+
};
|
|
39
|
+
// Rescan if directory changed
|
|
40
|
+
if (directoryChanged) {
|
|
41
|
+
this.indexingCompleteSubject.next(false);
|
|
42
|
+
void this.scanAllFiles();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
this.events$ = this.scanEventsSubject.asObservable();
|
|
46
|
+
this.indexingComplete$ = this.indexingCompleteSubject.asObservable();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start the indexer and perform initial scan
|
|
50
|
+
*/
|
|
51
|
+
start() {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
this.indexingCompleteSubject.next(false);
|
|
54
|
+
const fileSystemEvents$ = this.buildFileSystemEvents$();
|
|
55
|
+
this.fileSub = fileSystemEvents$.subscribe((event) => {
|
|
56
|
+
this.scanEventsSubject.next(event);
|
|
57
|
+
});
|
|
58
|
+
yield this.scanAllFiles();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stop the indexer and clean up subscriptions
|
|
63
|
+
*/
|
|
64
|
+
stop() {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
(_a = this.fileSub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
67
|
+
this.fileSub = null;
|
|
68
|
+
(_b = this.configSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe();
|
|
69
|
+
this.configSubscription = null;
|
|
70
|
+
this.indexingCompleteSubject.complete();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear cache and rescan all files
|
|
74
|
+
*/
|
|
75
|
+
resync() {
|
|
76
|
+
this.frontmatterCache.clear();
|
|
77
|
+
this.indexingCompleteSubject.next(false);
|
|
78
|
+
void this.scanAllFiles();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Scan all markdown files in the configured directory
|
|
82
|
+
*/
|
|
83
|
+
scanAllFiles() {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
const allFiles = this.vault.getMarkdownFiles();
|
|
86
|
+
const relevantFiles = allFiles.filter((file) => this.isRelevantFile(file));
|
|
87
|
+
const events$ = from(relevantFiles).pipe(mergeMap((file) => __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
try {
|
|
89
|
+
return yield this.buildEvent(file);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error(`Error processing file ${file.path}:`, error);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}), this.config.scanConcurrency), filter((event) => event !== null), toArray());
|
|
96
|
+
try {
|
|
97
|
+
const allEvents = yield lastValueFrom(events$);
|
|
98
|
+
for (const event of allEvents) {
|
|
99
|
+
this.scanEventsSubject.next(event);
|
|
100
|
+
}
|
|
101
|
+
this.indexingCompleteSubject.next(true);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("❌ Error during file scanning:", error);
|
|
105
|
+
this.indexingCompleteSubject.next(true);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create an observable from a vault event
|
|
111
|
+
*/
|
|
112
|
+
fromVaultEvent(eventName) {
|
|
113
|
+
if (eventName === "create") {
|
|
114
|
+
return fromEventPattern((handler) => this.vault.on("create", handler), (handler) => this.vault.off("create", handler));
|
|
115
|
+
}
|
|
116
|
+
if (eventName === "modify") {
|
|
117
|
+
return fromEventPattern((handler) => this.vault.on("modify", handler), (handler) => this.vault.off("modify", handler));
|
|
118
|
+
}
|
|
119
|
+
if (eventName === "delete") {
|
|
120
|
+
return fromEventPattern((handler) => this.vault.on("delete", handler), (handler) => this.vault.off("delete", handler));
|
|
121
|
+
}
|
|
122
|
+
// eventName === "rename"
|
|
123
|
+
return fromEventPattern((handler) => this.vault.on("rename", handler), (handler) => this.vault.off("rename", handler)).pipe(map(([file]) => file));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Type guard to check if file is a markdown file
|
|
127
|
+
*/
|
|
128
|
+
static isMarkdownFile(f) {
|
|
129
|
+
return f instanceof TFile && f.extension === "md";
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Filter to only relevant markdown files in configured directory
|
|
133
|
+
*/
|
|
134
|
+
toRelevantFiles() {
|
|
135
|
+
return (source) => source.pipe(filter((f) => Indexer.isMarkdownFile(f)), filter((f) => this.isRelevantFile(f)));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Debounce events by file path
|
|
139
|
+
*/
|
|
140
|
+
debounceByPath(ms, key) {
|
|
141
|
+
return (source) => source.pipe(groupBy(key), mergeMap((g$) => g$.pipe(debounceTime(ms))));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build the file system events observable stream
|
|
145
|
+
*/
|
|
146
|
+
buildFileSystemEvents$() {
|
|
147
|
+
const created$ = this.fromVaultEvent("create").pipe(this.toRelevantFiles());
|
|
148
|
+
const modified$ = this.fromVaultEvent("modify").pipe(this.toRelevantFiles());
|
|
149
|
+
const deleted$ = this.fromVaultEvent("delete").pipe(this.toRelevantFiles());
|
|
150
|
+
const renamed$ = fromEventPattern((handler) => this.vault.on("rename", handler), (handler) => this.vault.off("rename", handler));
|
|
151
|
+
const changedIntents$ = merge(created$, modified$).pipe(this.debounceByPath(this.config.debounceMs, (f) => f.path), map((file) => ({ kind: "changed", file, path: file.path })));
|
|
152
|
+
const deletedIntents$ = deleted$.pipe(map((file) => ({ kind: "deleted", path: file.path })));
|
|
153
|
+
const renamedIntents$ = renamed$.pipe(map(([f, oldPath]) => [f, oldPath]), filter(([f]) => Indexer.isMarkdownFile(f) && this.isRelevantFile(f)), mergeMap(([f, oldPath]) => [
|
|
154
|
+
{ kind: "deleted", path: oldPath },
|
|
155
|
+
{ kind: "changed", file: f, path: f.path, oldPath },
|
|
156
|
+
]));
|
|
157
|
+
const intents$ = merge(changedIntents$, deletedIntents$, renamedIntents$);
|
|
158
|
+
return intents$.pipe(switchMap((intent) => {
|
|
159
|
+
if (intent.kind === "deleted") {
|
|
160
|
+
this.frontmatterCache.delete(intent.path);
|
|
161
|
+
return of({ type: "file-deleted", filePath: intent.path });
|
|
162
|
+
}
|
|
163
|
+
return from(this.buildEvent(intent.file, intent.oldPath)).pipe(filter((e) => e !== null));
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Build an indexer event from a file
|
|
168
|
+
*/
|
|
169
|
+
buildEvent(file, oldPath) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
var _a;
|
|
172
|
+
const cache = this.metadataCache.getFileCache(file);
|
|
173
|
+
if (!cache || !cache.frontmatter)
|
|
174
|
+
return null;
|
|
175
|
+
const { frontmatter } = cache;
|
|
176
|
+
const oldFrontmatter = this.frontmatterCache.get(file.path);
|
|
177
|
+
const source = {
|
|
178
|
+
filePath: file.path,
|
|
179
|
+
mtime: file.stat.mtime,
|
|
180
|
+
frontmatter,
|
|
181
|
+
folder: ((_a = file.parent) === null || _a === void 0 ? void 0 : _a.path) || "",
|
|
182
|
+
};
|
|
183
|
+
const event = {
|
|
184
|
+
type: "file-changed",
|
|
185
|
+
filePath: file.path,
|
|
186
|
+
oldPath,
|
|
187
|
+
source,
|
|
188
|
+
oldFrontmatter,
|
|
189
|
+
frontmatterDiff: oldFrontmatter
|
|
190
|
+
? compareFrontmatter(oldFrontmatter, frontmatter, this.config.excludedDiffProps)
|
|
191
|
+
: undefined,
|
|
192
|
+
};
|
|
193
|
+
// Update cache
|
|
194
|
+
this.frontmatterCache.set(file.path, Object.assign({}, frontmatter));
|
|
195
|
+
return event;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if file is in the configured directory
|
|
200
|
+
*/
|
|
201
|
+
isRelevantFile(file) {
|
|
202
|
+
return isFileInConfiguredDirectory(file.path, this.config.directory);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/core/indexer.ts"],"names":[],"mappings":"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","sourcesContent":["import { type App, type MetadataCache, type TAbstractFile, TFile, type Vault } from \"obsidian\";\nimport {\n\ttype BehaviorSubject,\n\tfrom,\n\tfromEventPattern,\n\tlastValueFrom,\n\tmerge,\n\ttype Observable,\n\tof,\n\tBehaviorSubject as RxBehaviorSubject,\n\tSubject,\n\ttype Subscription,\n} from \"rxjs\";\nimport { debounceTime, filter, groupBy, map, mergeMap, switchMap, toArray } from \"rxjs/operators\";\nimport { isFileInConfiguredDirectory } from \"../file/file\";\nimport { compareFrontmatter, type FrontmatterDiff } from \"../file/frontmatter-diff\";\n\n/**\n * Generic frontmatter object type for indexer\n */\nexport type IndexerFrontmatter = Record<string, unknown>;\n\n/**\n * Configuration for the generic indexer\n */\nexport interface IndexerConfig {\n\t/**\n\t * Directory to scan for files (e.g., \"Calendar\", \"Notes\")\n\t * If empty string or undefined, scans entire vault\n\t */\n\tdirectory?: string;\n\n\t/**\n\t * Properties to exclude when comparing frontmatter diffs\n\t */\n\texcludedDiffProps?: Set<string>;\n\n\t/**\n\t * Concurrency limit for file scanning operations\n\t */\n\tscanConcurrency?: number;\n\n\t/**\n\t * Debounce time in milliseconds for file change events\n\t */\n\tdebounceMs?: number;\n}\n\n/**\n * Raw file source with frontmatter and metadata\n */\nexport interface FileSource {\n\tfilePath: string;\n\tmtime: number;\n\tfrontmatter: IndexerFrontmatter;\n\tfolder: string;\n}\n\n/**\n * Types of indexer events\n */\nexport type IndexerEventType = \"file-changed\" | \"file-deleted\";\n\n/**\n * Generic indexer event\n */\nexport interface IndexerEvent {\n\ttype: IndexerEventType;\n\tfilePath: string;\n\toldPath?: string;\n\tsource?: FileSource;\n\toldFrontmatter?: IndexerFrontmatter;\n\tfrontmatterDiff?: FrontmatterDiff;\n}\n\ntype VaultEvent = \"create\" | \"modify\" | \"delete\" | \"rename\";\n\ntype FileIntent =\n\t| { kind: \"changed\"; file: TFile; path: string; oldPath?: string }\n\t| { kind: \"deleted\"; path: string };\n\n/**\n * Generic indexer that listens to Obsidian vault events and emits\n * RxJS observables with frontmatter diffs and metadata.\n *\n * This indexer is framework-agnostic and can be used by any plugin\n * that needs to track file changes with frontmatter.\n */\nexport class Indexer {\n\tprivate config: Required<IndexerConfig>;\n\tprivate fileSub: Subscription | null = null;\n\tprivate configSubscription: Subscription | null = null;\n\tprivate vault: Vault;\n\tprivate metadataCache: MetadataCache;\n\tprivate scanEventsSubject = new Subject<IndexerEvent>();\n\tprivate indexingCompleteSubject = new RxBehaviorSubject<boolean>(false);\n\tprivate frontmatterCache: Map<string, IndexerFrontmatter> = new Map();\n\n\tpublic readonly events$: Observable<IndexerEvent>;\n\tpublic readonly indexingComplete$: Observable<boolean>;\n\n\tconstructor(_app: App, configStore: BehaviorSubject<IndexerConfig>) {\n\t\tthis.vault = _app.vault;\n\t\tthis.metadataCache = _app.metadataCache;\n\n\t\t// Set defaults\n\t\tthis.config = {\n\t\t\tdirectory: configStore.value.directory || \"\",\n\t\t\texcludedDiffProps: configStore.value.excludedDiffProps || new Set(),\n\t\t\tscanConcurrency: configStore.value.scanConcurrency || 10,\n\t\t\tdebounceMs: configStore.value.debounceMs || 100,\n\t\t};\n\n\t\t// Subscribe to config changes\n\t\tthis.configSubscription = configStore.subscribe((newConfig) => {\n\t\t\tconst directoryChanged = this.config.directory !== (newConfig.directory || \"\");\n\n\t\t\tthis.config = {\n\t\t\t\tdirectory: newConfig.directory || \"\",\n\t\t\t\texcludedDiffProps: newConfig.excludedDiffProps || new Set(),\n\t\t\t\tscanConcurrency: newConfig.scanConcurrency || 10,\n\t\t\t\tdebounceMs: newConfig.debounceMs || 100,\n\t\t\t};\n\n\t\t\t// Rescan if directory changed\n\t\t\tif (directoryChanged) {\n\t\t\t\tthis.indexingCompleteSubject.next(false);\n\t\t\t\tvoid this.scanAllFiles();\n\t\t\t}\n\t\t});\n\n\t\tthis.events$ = this.scanEventsSubject.asObservable();\n\t\tthis.indexingComplete$ = this.indexingCompleteSubject.asObservable();\n\t}\n\n\t/**\n\t * Start the indexer and perform initial scan\n\t */\n\tasync start(): Promise<void> {\n\t\tthis.indexingCompleteSubject.next(false);\n\n\t\tconst fileSystemEvents$ = this.buildFileSystemEvents$();\n\n\t\tthis.fileSub = fileSystemEvents$.subscribe((event) => {\n\t\t\tthis.scanEventsSubject.next(event);\n\t\t});\n\n\t\tawait this.scanAllFiles();\n\t}\n\n\t/**\n\t * Stop the indexer and clean up subscriptions\n\t */\n\tstop(): void {\n\t\tthis.fileSub?.unsubscribe();\n\t\tthis.fileSub = null;\n\t\tthis.configSubscription?.unsubscribe();\n\t\tthis.configSubscription = null;\n\t\tthis.indexingCompleteSubject.complete();\n\t}\n\n\t/**\n\t * Clear cache and rescan all files\n\t */\n\tresync(): void {\n\t\tthis.frontmatterCache.clear();\n\t\tthis.indexingCompleteSubject.next(false);\n\t\tvoid this.scanAllFiles();\n\t}\n\n\t/**\n\t * Scan all markdown files in the configured directory\n\t */\n\tprivate async scanAllFiles(): Promise<void> {\n\t\tconst allFiles = this.vault.getMarkdownFiles();\n\t\tconst relevantFiles = allFiles.filter((file) => this.isRelevantFile(file));\n\n\t\tconst events$ = from(relevantFiles).pipe(\n\t\t\tmergeMap(async (file) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.buildEvent(file);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Error processing file ${file.path}:`, error);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}, this.config.scanConcurrency),\n\t\t\tfilter((event): event is IndexerEvent => event !== null),\n\t\t\ttoArray()\n\t\t);\n\n\t\ttry {\n\t\t\tconst allEvents = await lastValueFrom(events$);\n\n\t\t\tfor (const event of allEvents) {\n\t\t\t\tthis.scanEventsSubject.next(event);\n\t\t\t}\n\n\t\t\tthis.indexingCompleteSubject.next(true);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"❌ Error during file scanning:\", error);\n\t\t\tthis.indexingCompleteSubject.next(true);\n\t\t}\n\t}\n\n\t/**\n\t * Create an observable from a vault event\n\t */\n\tprivate fromVaultEvent(eventName: VaultEvent): Observable<TAbstractFile> {\n\t\tif (eventName === \"create\") {\n\t\t\treturn fromEventPattern<TAbstractFile>(\n\t\t\t\t(handler) => this.vault.on(\"create\", handler),\n\t\t\t\t(handler) => this.vault.off(\"create\", handler)\n\t\t\t);\n\t\t}\n\n\t\tif (eventName === \"modify\") {\n\t\t\treturn fromEventPattern<TAbstractFile>(\n\t\t\t\t(handler) => this.vault.on(\"modify\", handler),\n\t\t\t\t(handler) => this.vault.off(\"modify\", handler)\n\t\t\t);\n\t\t}\n\n\t\tif (eventName === \"delete\") {\n\t\t\treturn fromEventPattern<TAbstractFile>(\n\t\t\t\t(handler) => this.vault.on(\"delete\", handler),\n\t\t\t\t(handler) => this.vault.off(\"delete\", handler)\n\t\t\t);\n\t\t}\n\n\t\t// eventName === \"rename\"\n\t\treturn fromEventPattern<[TAbstractFile, string]>(\n\t\t\t(handler) => this.vault.on(\"rename\", handler),\n\t\t\t(handler) => this.vault.off(\"rename\", handler)\n\t\t).pipe(map(([file]) => file));\n\t}\n\n\t/**\n\t * Type guard to check if file is a markdown file\n\t */\n\tprivate static isMarkdownFile(f: TAbstractFile): f is TFile {\n\t\treturn f instanceof TFile && f.extension === \"md\";\n\t}\n\n\t/**\n\t * Filter to only relevant markdown files in configured directory\n\t */\n\tprivate toRelevantFiles<T extends TAbstractFile>() {\n\t\treturn (source: Observable<T>) =>\n\t\t\tsource.pipe(\n\t\t\t\tfilter((f: TAbstractFile): f is TFile => Indexer.isMarkdownFile(f)),\n\t\t\t\tfilter((f) => this.isRelevantFile(f))\n\t\t\t);\n\t}\n\n\t/**\n\t * Debounce events by file path\n\t */\n\tprivate debounceByPath<T>(ms: number, key: (x: T) => string) {\n\t\treturn (source: Observable<T>) =>\n\t\t\tsource.pipe(\n\t\t\t\tgroupBy(key),\n\t\t\t\tmergeMap((g$) => g$.pipe(debounceTime(ms)))\n\t\t\t);\n\t}\n\n\t/**\n\t * Build the file system events observable stream\n\t */\n\tprivate buildFileSystemEvents$(): Observable<IndexerEvent> {\n\t\tconst created$ = this.fromVaultEvent(\"create\").pipe(this.toRelevantFiles());\n\t\tconst modified$ = this.fromVaultEvent(\"modify\").pipe(this.toRelevantFiles());\n\t\tconst deleted$ = this.fromVaultEvent(\"delete\").pipe(this.toRelevantFiles());\n\n\t\tconst renamed$ = fromEventPattern<[TAbstractFile, string]>(\n\t\t\t(handler) => this.vault.on(\"rename\", handler),\n\t\t\t(handler) => this.vault.off(\"rename\", handler)\n\t\t);\n\n\t\tconst changedIntents$ = merge(created$, modified$).pipe(\n\t\t\tthis.debounceByPath(this.config.debounceMs, (f) => f.path),\n\t\t\tmap((file): FileIntent => ({ kind: \"changed\", file, path: file.path }))\n\t\t);\n\n\t\tconst deletedIntents$ = deleted$.pipe(\n\t\t\tmap((file): FileIntent => ({ kind: \"deleted\", path: file.path }))\n\t\t);\n\n\t\tconst renamedIntents$ = renamed$.pipe(\n\t\t\tmap(([f, oldPath]) => [f, oldPath] as const),\n\t\t\tfilter(([f]) => Indexer.isMarkdownFile(f) && this.isRelevantFile(f)),\n\t\t\tmergeMap(([f, oldPath]) => [\n\t\t\t\t{ kind: \"deleted\", path: oldPath } as FileIntent,\n\t\t\t\t{ kind: \"changed\", file: f, path: f.path, oldPath } as FileIntent,\n\t\t\t])\n\t\t);\n\n\t\tconst intents$ = merge(changedIntents$, deletedIntents$, renamedIntents$);\n\n\t\treturn intents$.pipe(\n\t\t\tswitchMap((intent) => {\n\t\t\t\tif (intent.kind === \"deleted\") {\n\t\t\t\t\tthis.frontmatterCache.delete(intent.path);\n\t\t\t\t\treturn of<IndexerEvent>({ type: \"file-deleted\", filePath: intent.path });\n\t\t\t\t}\n\n\t\t\t\treturn from(this.buildEvent(intent.file, intent.oldPath)).pipe(\n\t\t\t\t\tfilter((e): e is IndexerEvent => e !== null)\n\t\t\t\t);\n\t\t\t})\n\t\t);\n\t}\n\n\t/**\n\t * Build an indexer event from a file\n\t */\n\tprivate async buildEvent(file: TFile, oldPath?: string): Promise<IndexerEvent | null> {\n\t\tconst cache = this.metadataCache.getFileCache(file);\n\t\tif (!cache || !cache.frontmatter) return null;\n\n\t\tconst { frontmatter } = cache;\n\t\tconst oldFrontmatter = this.frontmatterCache.get(file.path);\n\n\t\tconst source: FileSource = {\n\t\t\tfilePath: file.path,\n\t\t\tmtime: file.stat.mtime,\n\t\t\tfrontmatter,\n\t\t\tfolder: file.parent?.path || \"\",\n\t\t};\n\n\t\tconst event: IndexerEvent = {\n\t\t\ttype: \"file-changed\",\n\t\t\tfilePath: file.path,\n\t\t\toldPath,\n\t\t\tsource,\n\t\t\toldFrontmatter,\n\t\t\tfrontmatterDiff: oldFrontmatter\n\t\t\t\t? compareFrontmatter(oldFrontmatter, frontmatter, this.config.excludedDiffProps)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\t// Update cache\n\t\tthis.frontmatterCache.set(file.path, { ...frontmatter });\n\n\t\treturn event;\n\t}\n\n\t/**\n\t * Check if file is in the configured directory\n\t */\n\tprivate isRelevantFile(file: TFile): boolean {\n\t\treturn isFileInConfiguredDirectory(file.path, this.config.directory);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type Frontmatter = Record<string, unknown>;
|
|
2
|
+
export interface FrontmatterChange {
|
|
3
|
+
key: string;
|
|
4
|
+
oldValue: unknown;
|
|
5
|
+
newValue: unknown;
|
|
6
|
+
changeType: "added" | "modified" | "deleted";
|
|
7
|
+
}
|
|
8
|
+
export interface FrontmatterDiff {
|
|
9
|
+
hasChanges: boolean;
|
|
10
|
+
changes: FrontmatterChange[];
|
|
11
|
+
added: FrontmatterChange[];
|
|
12
|
+
modified: FrontmatterChange[];
|
|
13
|
+
deleted: FrontmatterChange[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Compares two frontmatter objects and returns a detailed diff.
|
|
17
|
+
* Excludes specified properties from comparison (e.g., Prisma-managed properties).
|
|
18
|
+
*
|
|
19
|
+
* @param oldFrontmatter - The original frontmatter
|
|
20
|
+
* @param newFrontmatter - The updated frontmatter
|
|
21
|
+
* @param excludeProps - Set of property keys to exclude from comparison
|
|
22
|
+
* @returns Detailed diff with categorized changes
|
|
23
|
+
*/
|
|
24
|
+
export declare function compareFrontmatter(oldFrontmatter: Frontmatter, newFrontmatter: Frontmatter, excludeProps?: Set<string>): FrontmatterDiff;
|
|
25
|
+
/**
|
|
26
|
+
* Merges multiple frontmatter diffs into a single accumulated diff.
|
|
27
|
+
* Later diffs override earlier ones for the same key.
|
|
28
|
+
*
|
|
29
|
+
* @param diffs - Array of diffs to merge (in chronological order)
|
|
30
|
+
* @returns A single merged diff containing all accumulated changes
|
|
31
|
+
*/
|
|
32
|
+
export declare function mergeFrontmatterDiffs(diffs: FrontmatterDiff[]): FrontmatterDiff;
|
|
33
|
+
/**
|
|
34
|
+
* Formats a frontmatter change for display in a modal.
|
|
35
|
+
* Returns a human-readable string describing the change.
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatChangeForDisplay(change: FrontmatterChange): string;
|
|
38
|
+
//# sourceMappingURL=frontmatter-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-diff.d.ts","sourceRoot":"","sources":["../../src/file/frontmatter-diff.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,MAAM,WAAW,iBAAiB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CACjC,cAAc,EAAE,WAAW,EAC3B,cAAc,EAAE,WAAW,EAC3B,YAAY,GAAE,GAAG,CAAC,MAAM,CAAa,GACnC,eAAe,CAwDjB;AAkCD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,CA8C/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAkBxE"}
|