@real1ty-obsidian-plugins/utils 2.22.0 → 2.24.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/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/components/whats-new-modal.d.ts +57 -0
- package/dist/components/whats-new-modal.d.ts.map +1 -0
- package/dist/components/whats-new-modal.js +108 -0
- package/dist/components/whats-new-modal.js.map +1 -0
- package/dist/file/templater.d.ts +5 -0
- package/dist/file/templater.d.ts.map +1 -1
- package/dist/file/templater.js +13 -4
- package/dist/file/templater.js.map +1 -1
- package/dist/string/changelog-parser.d.ts +17 -0
- package/dist/string/changelog-parser.d.ts.map +1 -0
- package/dist/string/changelog-parser.js +77 -0
- package/dist/string/changelog-parser.js.map +1 -0
- package/dist/string/index.d.ts +1 -0
- package/dist/string/index.d.ts.map +1 -1
- package/dist/string/index.js +1 -0
- package/dist/string/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/whats-new-modal.ts +175 -0
- package/src/file/templater.ts +20 -7
- package/src/string/changelog-parser.ts +100 -0
- package/src/string/index.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,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;AACjC,cAAc,mBAAmB,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,iCAAiC,CAAC;AAChD,cAAc,kBAAkB,CAAC","sourcesContent":["export * from \"./frontmatter-propagation-modal\";\nexport * 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;AACjC,cAAc,mBAAmB,CAAC","sourcesContent":["export * from \"./frontmatter-propagation-modal\";\nexport * from \"./input-managers\";\nexport * from \"./whats-new-modal\";\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { App, Plugin } from "obsidian";
|
|
2
|
+
import { Modal } from "obsidian";
|
|
3
|
+
export interface WhatsNewModalConfig {
|
|
4
|
+
/**
|
|
5
|
+
* The CSS class prefix/suffix to use for styling.
|
|
6
|
+
* Example: "custom-calendar" will generate classes like "custom-calendar-whats-new-modal"
|
|
7
|
+
*/
|
|
8
|
+
cssPrefix: string;
|
|
9
|
+
/**
|
|
10
|
+
* Display name of the plugin.
|
|
11
|
+
* Example: "Custom Calendar"
|
|
12
|
+
*/
|
|
13
|
+
pluginName: string;
|
|
14
|
+
/**
|
|
15
|
+
* Raw changelog markdown content to parse.
|
|
16
|
+
*/
|
|
17
|
+
changelogContent: string;
|
|
18
|
+
/**
|
|
19
|
+
* Links to external resources.
|
|
20
|
+
*/
|
|
21
|
+
links: {
|
|
22
|
+
/**
|
|
23
|
+
* URL to support/donate page.
|
|
24
|
+
*/
|
|
25
|
+
support?: string;
|
|
26
|
+
/**
|
|
27
|
+
* URL to full changelog page.
|
|
28
|
+
*/
|
|
29
|
+
changelog?: string;
|
|
30
|
+
/**
|
|
31
|
+
* URL to documentation.
|
|
32
|
+
*/
|
|
33
|
+
documentation?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generic "What's New" modal that displays changelog entries between versions.
|
|
38
|
+
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
39
|
+
*/
|
|
40
|
+
export declare class WhatsNewModal extends Modal {
|
|
41
|
+
private plugin;
|
|
42
|
+
private config;
|
|
43
|
+
private fromVersion;
|
|
44
|
+
private toVersion;
|
|
45
|
+
constructor(app: App, plugin: Plugin, config: WhatsNewModalConfig, fromVersion: string, toVersion: string);
|
|
46
|
+
/**
|
|
47
|
+
* Helper to create CSS class names with the configured prefix.
|
|
48
|
+
*/
|
|
49
|
+
private cls;
|
|
50
|
+
/**
|
|
51
|
+
* Helper to add CSS class to an element.
|
|
52
|
+
*/
|
|
53
|
+
private addCls;
|
|
54
|
+
onOpen(): Promise<void>;
|
|
55
|
+
onClose(): void;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=whats-new-modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whats-new-modal.d.ts","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAoB,KAAK,EAAE,MAAM,UAAU,CAAC;AAGnD,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,KAAK,EAAE;QACN;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB;;WAEG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACF;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGtC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,SAAS;gBAJjB,GAAG,EAAE,GAAG,EACA,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB,EAC3B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM;IAK1B;;OAEG;IACH,OAAO,CAAC,GAAG;IAIX;;OAEG;IACH,OAAO,CAAC,MAAM;IAIR,MAAM;IAmGZ,OAAO;CAGP"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { MarkdownRenderer, Modal } from "obsidian";
|
|
3
|
+
import { formatChangelogSections, getChangelogSince } from "../string/changelog-parser";
|
|
4
|
+
/**
|
|
5
|
+
* Generic "What's New" modal that displays changelog entries between versions.
|
|
6
|
+
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
7
|
+
*/
|
|
8
|
+
export class WhatsNewModal extends Modal {
|
|
9
|
+
constructor(app, plugin, config, fromVersion, toVersion) {
|
|
10
|
+
super(app);
|
|
11
|
+
this.plugin = plugin;
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.fromVersion = fromVersion;
|
|
14
|
+
this.toVersion = toVersion;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Helper to create CSS class names with the configured prefix.
|
|
18
|
+
*/
|
|
19
|
+
cls(suffix) {
|
|
20
|
+
return `${this.config.cssPrefix}-${suffix}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Helper to add CSS class to an element.
|
|
24
|
+
*/
|
|
25
|
+
addCls(el, suffix) {
|
|
26
|
+
el.classList.add(this.cls(suffix));
|
|
27
|
+
}
|
|
28
|
+
onOpen() {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const { contentEl } = this;
|
|
31
|
+
contentEl.empty();
|
|
32
|
+
this.addCls(contentEl, "whats-new-modal");
|
|
33
|
+
// Header section
|
|
34
|
+
const header = contentEl.createDiv({ cls: this.cls("whats-new-header") });
|
|
35
|
+
header.createEl("h2", {
|
|
36
|
+
text: `${this.config.pluginName} updated to v${this.toVersion}`,
|
|
37
|
+
});
|
|
38
|
+
header.createEl("p", {
|
|
39
|
+
text: `Changes since v${this.fromVersion}`,
|
|
40
|
+
cls: this.cls("whats-new-subtitle"),
|
|
41
|
+
});
|
|
42
|
+
// Support section (optional)
|
|
43
|
+
if (this.config.links.support) {
|
|
44
|
+
const supportSection = contentEl.createDiv({
|
|
45
|
+
cls: this.cls("whats-new-support"),
|
|
46
|
+
});
|
|
47
|
+
supportSection.createEl("h3", { text: "Support My Work" });
|
|
48
|
+
const supportText = supportSection.createEl("p");
|
|
49
|
+
supportText.createSpan({ text: "If you enjoy using this plugin, please consider " });
|
|
50
|
+
supportText.createEl("a", {
|
|
51
|
+
text: "supporting my work",
|
|
52
|
+
href: this.config.links.support,
|
|
53
|
+
});
|
|
54
|
+
supportText.createSpan({
|
|
55
|
+
text: ". Your support helps keep this plugin maintained and improved!",
|
|
56
|
+
});
|
|
57
|
+
contentEl.createEl("hr");
|
|
58
|
+
}
|
|
59
|
+
// Changelog content
|
|
60
|
+
const changelogSections = getChangelogSince(this.config.changelogContent, this.fromVersion, this.toVersion);
|
|
61
|
+
if (changelogSections.length === 0) {
|
|
62
|
+
contentEl.createEl("p", {
|
|
63
|
+
text: "No significant changes found in this update.",
|
|
64
|
+
cls: this.cls("whats-new-empty"),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const changelogContainer = contentEl.createDiv({
|
|
69
|
+
cls: this.cls("whats-new-content"),
|
|
70
|
+
});
|
|
71
|
+
const markdownContent = formatChangelogSections(changelogSections);
|
|
72
|
+
yield MarkdownRenderer.render(this.app, markdownContent, changelogContainer, "/", this.plugin);
|
|
73
|
+
}
|
|
74
|
+
// Action buttons
|
|
75
|
+
const buttonContainer = contentEl.createDiv({
|
|
76
|
+
cls: this.cls("whats-new-buttons"),
|
|
77
|
+
});
|
|
78
|
+
// Full changelog button (optional)
|
|
79
|
+
if (this.config.links.changelog) {
|
|
80
|
+
const changelogBtn = buttonContainer.createEl("button", {
|
|
81
|
+
text: "Full Changelog",
|
|
82
|
+
});
|
|
83
|
+
changelogBtn.addEventListener("click", () => {
|
|
84
|
+
window.open(this.config.links.changelog, "_blank");
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Documentation button (optional)
|
|
88
|
+
if (this.config.links.documentation) {
|
|
89
|
+
const docsBtn = buttonContainer.createEl("button", {
|
|
90
|
+
text: "Documentation",
|
|
91
|
+
});
|
|
92
|
+
docsBtn.addEventListener("click", () => {
|
|
93
|
+
window.open(this.config.links.documentation, "_blank");
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Close button (always present)
|
|
97
|
+
const closeBtn = buttonContainer.createEl("button", {
|
|
98
|
+
text: "Close",
|
|
99
|
+
cls: this.cls("mod-cta"),
|
|
100
|
+
});
|
|
101
|
+
closeBtn.addEventListener("click", () => this.close());
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
onClose() {
|
|
105
|
+
this.contentEl.empty();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=whats-new-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whats-new-modal.js","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"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","sourcesContent":["import type { App, Plugin } from \"obsidian\";\nimport { MarkdownRenderer, Modal } from \"obsidian\";\nimport { formatChangelogSections, getChangelogSince } from \"../string/changelog-parser\";\n\nexport interface WhatsNewModalConfig {\n\t/**\n\t * The CSS class prefix/suffix to use for styling.\n\t * Example: \"custom-calendar\" will generate classes like \"custom-calendar-whats-new-modal\"\n\t */\n\tcssPrefix: string;\n\n\t/**\n\t * Display name of the plugin.\n\t * Example: \"Custom Calendar\"\n\t */\n\tpluginName: string;\n\n\t/**\n\t * Raw changelog markdown content to parse.\n\t */\n\tchangelogContent: string;\n\n\t/**\n\t * Links to external resources.\n\t */\n\tlinks: {\n\t\t/**\n\t\t * URL to support/donate page.\n\t\t */\n\t\tsupport?: string;\n\n\t\t/**\n\t\t * URL to full changelog page.\n\t\t */\n\t\tchangelog?: string;\n\n\t\t/**\n\t\t * URL to documentation.\n\t\t */\n\t\tdocumentation?: string;\n\t};\n}\n\n/**\n * Generic \"What's New\" modal that displays changelog entries between versions.\n * Supports custom CSS prefixes, plugin names, and configurable links.\n */\nexport class WhatsNewModal extends Modal {\n\tconstructor(\n\t\tapp: App,\n\t\tprivate plugin: Plugin,\n\t\tprivate config: WhatsNewModalConfig,\n\t\tprivate fromVersion: string,\n\t\tprivate toVersion: string\n\t) {\n\t\tsuper(app);\n\t}\n\n\t/**\n\t * Helper to create CSS class names with the configured prefix.\n\t */\n\tprivate cls(suffix: string): string {\n\t\treturn `${this.config.cssPrefix}-${suffix}`;\n\t}\n\n\t/**\n\t * Helper to add CSS class to an element.\n\t */\n\tprivate addCls(el: HTMLElement, suffix: string): void {\n\t\tel.classList.add(this.cls(suffix));\n\t}\n\n\tasync onOpen() {\n\t\tconst { contentEl } = this;\n\t\tcontentEl.empty();\n\n\t\tthis.addCls(contentEl, \"whats-new-modal\");\n\n\t\t// Header section\n\t\tconst header = contentEl.createDiv({ cls: this.cls(\"whats-new-header\") });\n\t\theader.createEl(\"h2\", {\n\t\t\ttext: `${this.config.pluginName} updated to v${this.toVersion}`,\n\t\t});\n\n\t\theader.createEl(\"p\", {\n\t\t\ttext: `Changes since v${this.fromVersion}`,\n\t\t\tcls: this.cls(\"whats-new-subtitle\"),\n\t\t});\n\n\t\t// Support section (optional)\n\t\tif (this.config.links.support) {\n\t\t\tconst supportSection = contentEl.createDiv({\n\t\t\t\tcls: this.cls(\"whats-new-support\"),\n\t\t\t});\n\n\t\t\tsupportSection.createEl(\"h3\", { text: \"Support My Work\" });\n\n\t\t\tconst supportText = supportSection.createEl(\"p\");\n\t\t\tsupportText.createSpan({ text: \"If you enjoy using this plugin, please consider \" });\n\t\t\tsupportText.createEl(\"a\", {\n\t\t\t\ttext: \"supporting my work\",\n\t\t\t\thref: this.config.links.support,\n\t\t\t});\n\t\t\tsupportText.createSpan({\n\t\t\t\ttext: \". Your support helps keep this plugin maintained and improved!\",\n\t\t\t});\n\n\t\t\tcontentEl.createEl(\"hr\");\n\t\t}\n\n\t\t// Changelog content\n\t\tconst changelogSections = getChangelogSince(\n\t\t\tthis.config.changelogContent,\n\t\t\tthis.fromVersion,\n\t\t\tthis.toVersion\n\t\t);\n\n\t\tif (changelogSections.length === 0) {\n\t\t\tcontentEl.createEl(\"p\", {\n\t\t\t\ttext: \"No significant changes found in this update.\",\n\t\t\t\tcls: this.cls(\"whats-new-empty\"),\n\t\t\t});\n\t\t} else {\n\t\t\tconst changelogContainer = contentEl.createDiv({\n\t\t\t\tcls: this.cls(\"whats-new-content\"),\n\t\t\t});\n\n\t\t\tconst markdownContent = formatChangelogSections(changelogSections);\n\n\t\t\tawait MarkdownRenderer.render(\n\t\t\t\tthis.app,\n\t\t\t\tmarkdownContent,\n\t\t\t\tchangelogContainer,\n\t\t\t\t\"/\",\n\t\t\t\tthis.plugin\n\t\t\t);\n\t\t}\n\n\t\t// Action buttons\n\t\tconst buttonContainer = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-buttons\"),\n\t\t});\n\n\t\t// Full changelog button (optional)\n\t\tif (this.config.links.changelog) {\n\t\t\tconst changelogBtn = buttonContainer.createEl(\"button\", {\n\t\t\t\ttext: \"Full Changelog\",\n\t\t\t});\n\t\t\tchangelogBtn.addEventListener(\"click\", () => {\n\t\t\t\twindow.open(this.config.links.changelog, \"_blank\");\n\t\t\t});\n\t\t}\n\n\t\t// Documentation button (optional)\n\t\tif (this.config.links.documentation) {\n\t\t\tconst docsBtn = buttonContainer.createEl(\"button\", {\n\t\t\t\ttext: \"Documentation\",\n\t\t\t});\n\t\t\tdocsBtn.addEventListener(\"click\", () => {\n\t\t\t\twindow.open(this.config.links.documentation, \"_blank\");\n\t\t\t});\n\t\t}\n\n\t\t// Close button (always present)\n\t\tconst closeBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Close\",\n\t\t\tcls: this.cls(\"mod-cta\"),\n\t\t});\n\t\tcloseBtn.addEventListener(\"click\", () => this.close());\n\t}\n\n\tonClose() {\n\t\tthis.contentEl.empty();\n\t}\n}\n"]}
|
package/dist/file/templater.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export declare function isTemplaterAvailable(app: App): boolean;
|
|
|
13
13
|
* Checks if a template should be used based on availability and file existence.
|
|
14
14
|
*/
|
|
15
15
|
export declare function shouldUseTemplate(app: App, templatePath: string | undefined): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a file at the specified full path with optional frontmatter and content.
|
|
18
|
+
* Returns existing file if it already exists.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createFileAtPath(app: App, filePath: string, content?: string, frontmatter?: Record<string, unknown>): Promise<TFile>;
|
|
16
21
|
/**
|
|
17
22
|
* Creates a file manually with optional frontmatter and content.
|
|
18
23
|
* Returns existing file if it already exists.
|
|
@@ -1 +1 @@
|
|
|
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;AAiBlE,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;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAOrF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,GAAG,EACR,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,KAAK,CAAC,
|
|
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;AAiBlE,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;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAOrF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,KAAK,CAAC,CAkBhB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,GAAG,EACR,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAED,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,UAAQ,EACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CA8CvB;AAED,wBAAsB,sBAAsB,CAC3C,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,mBAAmB,GAC1B,OAAO,CAAC,KAAK,CAAC,CA4BhB"}
|
package/dist/file/templater.js
CHANGED
|
@@ -35,13 +35,11 @@ export function shouldUseTemplate(app, templatePath) {
|
|
|
35
35
|
app.vault.getFileByPath(templatePath));
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* Creates a file
|
|
38
|
+
* Creates a file at the specified full path with optional frontmatter and content.
|
|
39
39
|
* Returns existing file if it already exists.
|
|
40
40
|
*/
|
|
41
|
-
export function
|
|
41
|
+
export function createFileAtPath(app, filePath, content, frontmatter) {
|
|
42
42
|
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
const baseName = filename.replace(/\.md$/, "");
|
|
44
|
-
const filePath = `${targetDirectory}/${baseName}.md`;
|
|
45
43
|
// Check if file already exists
|
|
46
44
|
const existingFile = app.vault.getAbstractFileByPath(filePath);
|
|
47
45
|
if (existingFile instanceof TFile) {
|
|
@@ -59,6 +57,17 @@ export function createFileManually(app, targetDirectory, filename, content, fron
|
|
|
59
57
|
return file;
|
|
60
58
|
});
|
|
61
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a file manually with optional frontmatter and content.
|
|
62
|
+
* Returns existing file if it already exists.
|
|
63
|
+
*/
|
|
64
|
+
export function createFileManually(app, targetDirectory, filename, content, frontmatter) {
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
const baseName = filename.replace(/\.md$/, "");
|
|
67
|
+
const filePath = `${targetDirectory}/${baseName}.md`;
|
|
68
|
+
return createFileAtPath(app, filePath, content, frontmatter);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
62
71
|
export function createFromTemplate(app_1, templatePath_1, targetFolder_1, filename_1) {
|
|
63
72
|
return __awaiter(this, arguments, void 0, function* (app, templatePath, targetFolder, filename, openNewNote = false, frontmatter) {
|
|
64
73
|
const templater = yield waitForTemplater(app);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templater.js","sourceRoot":"","sources":["../../src/file/templater.ts"],"names":[],"mappings":";AAAA,OAAO,EAAY,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAC;AAE/E,MAAM,YAAY,GAAG,oBAAoB,CAAC;AAuB1C,SAAe,gBAAgB;yDAAC,GAAQ,EAAE,SAAS,GAAG,IAAI;;QACzD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QAE3E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS,EAAE,CAAC;YACzC,MAAM,IAAI,GAAQ,MAAA,MAAC,GAAW,CAAC,OAAO,0CAAE,SAAS,mDAAG,YAAY,CAAC,CAAC;YAClE,MAAM,GAAG,GAAG,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,SAAS,mCAAI,IAAI,CAAC;YAEpC,MAAM,QAAQ,GAAyB,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,6BAA6B,0CAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YACrF,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACpC,OAAO,EAAE,6BAA6B,EAAE,QAAQ,EAAE,CAAC;YACpD,CAAC;YACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CAAA;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAQ;;IAC5C,MAAM,QAAQ,GAAG,MAAA,MAAC,GAAW,CAAC,OAAO,0CAAE,SAAS,mDAAG,YAAY,CAAC,CAAC;IACjE,OAAO,CAAC,CAAC,QAAQ,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAQ,EAAE,YAAgC;IAC3E,OAAO,CAAC,CAAC,CACR,YAAY;QACZ,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE;QAC1B,oBAAoB,CAAC,GAAG,CAAC;QACzB,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CACrC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAgB,
|
|
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\";\nimport { waitForFileReady } from \"./file-utils\";\nimport { createFileContentWithFrontmatter } from \"./frontmatter-serialization\";\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\n/**\n * Checks if a template should be used based on availability and file existence.\n */\nexport function shouldUseTemplate(app: App, templatePath: string | undefined): boolean {\n\treturn !!(\n\t\ttemplatePath &&\n\t\ttemplatePath.trim() !== \"\" &&\n\t\tisTemplaterAvailable(app) &&\n\t\tapp.vault.getFileByPath(templatePath)\n\t);\n}\n\n/**\n * Creates a file at the specified full path with optional frontmatter and content.\n * Returns existing file if it already exists.\n */\nexport async function createFileAtPath(\n\tapp: App,\n\tfilePath: string,\n\tcontent?: string,\n\tfrontmatter?: Record<string, unknown>\n): Promise<TFile> {\n\t// Check if file already exists\n\tconst existingFile = app.vault.getAbstractFileByPath(filePath);\n\tif (existingFile instanceof TFile) {\n\t\treturn existingFile;\n\t}\n\n\tconst bodyContent = content || \"\";\n\n\tlet fileContent: string;\n\tif (frontmatter && Object.keys(frontmatter).length > 0) {\n\t\tfileContent = createFileContentWithFrontmatter(frontmatter, bodyContent);\n\t} else {\n\t\tfileContent = bodyContent;\n\t}\n\n\tconst file = await app.vault.create(filePath, fileContent);\n\treturn file;\n}\n\n/**\n * Creates a file manually with optional frontmatter and content.\n * Returns existing file if it already exists.\n */\nexport async function createFileManually(\n\tapp: App,\n\ttargetDirectory: string,\n\tfilename: string,\n\tcontent?: string,\n\tfrontmatter?: Record<string, unknown>\n): Promise<TFile> {\n\tconst baseName = filename.replace(/\\.md$/, \"\");\n\tconst filePath = `${targetDirectory}/${baseName}.md`;\n\n\treturn createFileAtPath(app, filePath, content, frontmatter);\n}\n\nexport async function createFromTemplate(\n\tapp: App,\n\ttemplatePath: string,\n\ttargetFolder?: string,\n\tfilename?: string,\n\topenNewNote = false,\n\tfrontmatter?: Record<string, unknown>\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\tif (!newFile) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (frontmatter && Object.keys(frontmatter).length > 0) {\n\t\t\tconst readyFile = await waitForFileReady(app, newFile.path);\n\n\t\t\tif (readyFile) {\n\t\t\t\tawait app.fileManager.processFrontMatter(readyFile, (fm) => {\n\t\t\t\t\tObject.assign(fm, frontmatter);\n\t\t\t\t});\n\t\t\t\treturn readyFile;\n\t\t\t}\n\t\t}\n\n\t\treturn newFile;\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\n\t// If content is provided, use manual creation to preserve the content\n\tif (content) {\n\t\treturn createFileManually(app, targetDirectory, finalFilename, content, frontmatter);\n\t}\n\n\t// Try to use template if requested and available\n\tif (useTemplater && shouldUseTemplate(app, templatePath)) {\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\tfalse,\n\t\t\tfrontmatter\n\t\t);\n\n\t\tif (templateFile) {\n\t\t\treturn templateFile;\n\t\t}\n\t}\n\n\treturn createFileManually(app, targetDirectory, finalFilename, content, frontmatter);\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface VersionSection {
|
|
2
|
+
version: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parses changelog markdown content into version sections.
|
|
7
|
+
* Each section starts with "## X.Y.Z" heading.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseChangelog(changelogContent: string): VersionSection[];
|
|
10
|
+
/**
|
|
11
|
+
* Gets all changelog sections between fromVersion (exclusive) and toVersion (inclusive).
|
|
12
|
+
* Sections are returned in reverse chronological order (newest first).
|
|
13
|
+
*/
|
|
14
|
+
export declare function getChangelogSince(changelogContent: string, fromVersion: string, toVersion: string): VersionSection[];
|
|
15
|
+
export declare function formatChangelogSections(sections: VersionSection[]): string;
|
|
16
|
+
export type { VersionSection };
|
|
17
|
+
//# sourceMappingURL=changelog-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog-parser.d.ts","sourceRoot":"","sources":["../../src/string/changelog-parser.ts"],"names":[],"mappings":"AAAA,UAAU,cAAc;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,cAAc,EAAE,CAiCzE;AAyBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GACf,cAAc,EAAE,CAQlB;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAY1E;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses changelog markdown content into version sections.
|
|
3
|
+
* Each section starts with "## X.Y.Z" heading.
|
|
4
|
+
*/
|
|
5
|
+
export function parseChangelog(changelogContent) {
|
|
6
|
+
const sections = [];
|
|
7
|
+
const lines = changelogContent.split("\n");
|
|
8
|
+
let currentVersion = null;
|
|
9
|
+
let currentContent = [];
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
const versionMatch = line.match(/^##\s+(\d+\.\d+\.\d+)/);
|
|
12
|
+
if (versionMatch) {
|
|
13
|
+
if (currentVersion !== null) {
|
|
14
|
+
sections.push({
|
|
15
|
+
version: currentVersion,
|
|
16
|
+
content: currentContent.join("\n").trim(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
currentVersion = versionMatch[1];
|
|
20
|
+
currentContent = [];
|
|
21
|
+
}
|
|
22
|
+
else if (currentVersion !== null) {
|
|
23
|
+
currentContent.push(line);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (currentVersion !== null && currentContent.length > 0) {
|
|
27
|
+
sections.push({
|
|
28
|
+
version: currentVersion,
|
|
29
|
+
content: currentContent.join("\n").trim(),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return sections;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Compares two semantic version strings.
|
|
36
|
+
* Returns:
|
|
37
|
+
* - negative if v1 < v2
|
|
38
|
+
* - 0 if v1 === v2
|
|
39
|
+
* - positive if v1 > v2
|
|
40
|
+
*/
|
|
41
|
+
function compareVersions(v1, v2) {
|
|
42
|
+
const parts1 = v1.split(".").map(Number);
|
|
43
|
+
const parts2 = v2.split(".").map(Number);
|
|
44
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
45
|
+
const num1 = parts1[i] || 0;
|
|
46
|
+
const num2 = parts2[i] || 0;
|
|
47
|
+
if (num1 !== num2) {
|
|
48
|
+
return num1 - num2;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets all changelog sections between fromVersion (exclusive) and toVersion (inclusive).
|
|
55
|
+
* Sections are returned in reverse chronological order (newest first).
|
|
56
|
+
*/
|
|
57
|
+
export function getChangelogSince(changelogContent, fromVersion, toVersion) {
|
|
58
|
+
const allSections = parseChangelog(changelogContent);
|
|
59
|
+
return allSections.filter((section) => {
|
|
60
|
+
const isAfterFrom = compareVersions(section.version, fromVersion) > 0;
|
|
61
|
+
const isBeforeOrEqualTo = compareVersions(section.version, toVersion) <= 0;
|
|
62
|
+
return isAfterFrom && isBeforeOrEqualTo;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function formatChangelogSections(sections) {
|
|
66
|
+
if (sections.length === 0) {
|
|
67
|
+
return "No changes found.";
|
|
68
|
+
}
|
|
69
|
+
return sections
|
|
70
|
+
.map((section) => {
|
|
71
|
+
// Escape Dataview inline queries to prevent parsing errors
|
|
72
|
+
const content = section.content.replace(/`=([^`]+)`/g, "`\\=$1`");
|
|
73
|
+
return `## ${section.version}\n\n${content}`;
|
|
74
|
+
})
|
|
75
|
+
.join("\n\n");
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=changelog-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog-parser.js","sourceRoot":"","sources":["../../src/string/changelog-parser.ts"],"names":[],"mappings":"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","sourcesContent":["interface VersionSection {\n\tversion: string;\n\tcontent: string;\n}\n\n/**\n * Parses changelog markdown content into version sections.\n * Each section starts with \"## X.Y.Z\" heading.\n */\nexport function parseChangelog(changelogContent: string): VersionSection[] {\n\tconst sections: VersionSection[] = [];\n\tconst lines = changelogContent.split(\"\\n\");\n\n\tlet currentVersion: string | null = null;\n\tlet currentContent: string[] = [];\n\n\tfor (const line of lines) {\n\t\tconst versionMatch = line.match(/^##\\s+(\\d+\\.\\d+\\.\\d+)/);\n\n\t\tif (versionMatch) {\n\t\t\tif (currentVersion !== null) {\n\t\t\t\tsections.push({\n\t\t\t\t\tversion: currentVersion,\n\t\t\t\t\tcontent: currentContent.join(\"\\n\").trim(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcurrentVersion = versionMatch[1];\n\t\t\tcurrentContent = [];\n\t\t} else if (currentVersion !== null) {\n\t\t\tcurrentContent.push(line);\n\t\t}\n\t}\n\n\tif (currentVersion !== null && currentContent.length > 0) {\n\t\tsections.push({\n\t\t\tversion: currentVersion,\n\t\t\tcontent: currentContent.join(\"\\n\").trim(),\n\t\t});\n\t}\n\n\treturn sections;\n}\n\n/**\n * Compares two semantic version strings.\n * Returns:\n * - negative if v1 < v2\n * - 0 if v1 === v2\n * - positive if v1 > v2\n */\nfunction compareVersions(v1: string, v2: string): number {\n\tconst parts1 = v1.split(\".\").map(Number);\n\tconst parts2 = v2.split(\".\").map(Number);\n\n\tfor (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n\t\tconst num1 = parts1[i] || 0;\n\t\tconst num2 = parts2[i] || 0;\n\n\t\tif (num1 !== num2) {\n\t\t\treturn num1 - num2;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n/**\n * Gets all changelog sections between fromVersion (exclusive) and toVersion (inclusive).\n * Sections are returned in reverse chronological order (newest first).\n */\nexport function getChangelogSince(\n\tchangelogContent: string,\n\tfromVersion: string,\n\ttoVersion: string\n): VersionSection[] {\n\tconst allSections = parseChangelog(changelogContent);\n\n\treturn allSections.filter((section) => {\n\t\tconst isAfterFrom = compareVersions(section.version, fromVersion) > 0;\n\t\tconst isBeforeOrEqualTo = compareVersions(section.version, toVersion) <= 0;\n\t\treturn isAfterFrom && isBeforeOrEqualTo;\n\t});\n}\n\nexport function formatChangelogSections(sections: VersionSection[]): string {\n\tif (sections.length === 0) {\n\t\treturn \"No changes found.\";\n\t}\n\n\treturn sections\n\t\t.map((section) => {\n\t\t\t// Escape Dataview inline queries to prevent parsing errors\n\t\t\tconst content = section.content.replace(/`=([^`]+)`/g, \"`\\\\=$1`\");\n\t\t\treturn `## ${section.version}\\n\\n${content}`;\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nexport type { VersionSection };\n"]}
|
package/dist/string/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC"}
|
package/dist/string/index.js
CHANGED
package/dist/string/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC","sourcesContent":["export * from \"./filename-utils\";\nexport * from \"./string\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC","sourcesContent":["export * from \"./changelog-parser\";\nexport * from \"./filename-utils\";\nexport * from \"./string\";\n"]}
|
package/package.json
CHANGED
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { App, Plugin } from "obsidian";
|
|
2
|
+
import { MarkdownRenderer, Modal } from "obsidian";
|
|
3
|
+
import { formatChangelogSections, getChangelogSince } from "../string/changelog-parser";
|
|
4
|
+
|
|
5
|
+
export interface WhatsNewModalConfig {
|
|
6
|
+
/**
|
|
7
|
+
* The CSS class prefix/suffix to use for styling.
|
|
8
|
+
* Example: "custom-calendar" will generate classes like "custom-calendar-whats-new-modal"
|
|
9
|
+
*/
|
|
10
|
+
cssPrefix: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Display name of the plugin.
|
|
14
|
+
* Example: "Custom Calendar"
|
|
15
|
+
*/
|
|
16
|
+
pluginName: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Raw changelog markdown content to parse.
|
|
20
|
+
*/
|
|
21
|
+
changelogContent: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Links to external resources.
|
|
25
|
+
*/
|
|
26
|
+
links: {
|
|
27
|
+
/**
|
|
28
|
+
* URL to support/donate page.
|
|
29
|
+
*/
|
|
30
|
+
support?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* URL to full changelog page.
|
|
34
|
+
*/
|
|
35
|
+
changelog?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* URL to documentation.
|
|
39
|
+
*/
|
|
40
|
+
documentation?: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generic "What's New" modal that displays changelog entries between versions.
|
|
46
|
+
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
47
|
+
*/
|
|
48
|
+
export class WhatsNewModal extends Modal {
|
|
49
|
+
constructor(
|
|
50
|
+
app: App,
|
|
51
|
+
private plugin: Plugin,
|
|
52
|
+
private config: WhatsNewModalConfig,
|
|
53
|
+
private fromVersion: string,
|
|
54
|
+
private toVersion: string
|
|
55
|
+
) {
|
|
56
|
+
super(app);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Helper to create CSS class names with the configured prefix.
|
|
61
|
+
*/
|
|
62
|
+
private cls(suffix: string): string {
|
|
63
|
+
return `${this.config.cssPrefix}-${suffix}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Helper to add CSS class to an element.
|
|
68
|
+
*/
|
|
69
|
+
private addCls(el: HTMLElement, suffix: string): void {
|
|
70
|
+
el.classList.add(this.cls(suffix));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async onOpen() {
|
|
74
|
+
const { contentEl } = this;
|
|
75
|
+
contentEl.empty();
|
|
76
|
+
|
|
77
|
+
this.addCls(contentEl, "whats-new-modal");
|
|
78
|
+
|
|
79
|
+
// Header section
|
|
80
|
+
const header = contentEl.createDiv({ cls: this.cls("whats-new-header") });
|
|
81
|
+
header.createEl("h2", {
|
|
82
|
+
text: `${this.config.pluginName} updated to v${this.toVersion}`,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
header.createEl("p", {
|
|
86
|
+
text: `Changes since v${this.fromVersion}`,
|
|
87
|
+
cls: this.cls("whats-new-subtitle"),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Support section (optional)
|
|
91
|
+
if (this.config.links.support) {
|
|
92
|
+
const supportSection = contentEl.createDiv({
|
|
93
|
+
cls: this.cls("whats-new-support"),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
supportSection.createEl("h3", { text: "Support My Work" });
|
|
97
|
+
|
|
98
|
+
const supportText = supportSection.createEl("p");
|
|
99
|
+
supportText.createSpan({ text: "If you enjoy using this plugin, please consider " });
|
|
100
|
+
supportText.createEl("a", {
|
|
101
|
+
text: "supporting my work",
|
|
102
|
+
href: this.config.links.support,
|
|
103
|
+
});
|
|
104
|
+
supportText.createSpan({
|
|
105
|
+
text: ". Your support helps keep this plugin maintained and improved!",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
contentEl.createEl("hr");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Changelog content
|
|
112
|
+
const changelogSections = getChangelogSince(
|
|
113
|
+
this.config.changelogContent,
|
|
114
|
+
this.fromVersion,
|
|
115
|
+
this.toVersion
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (changelogSections.length === 0) {
|
|
119
|
+
contentEl.createEl("p", {
|
|
120
|
+
text: "No significant changes found in this update.",
|
|
121
|
+
cls: this.cls("whats-new-empty"),
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
const changelogContainer = contentEl.createDiv({
|
|
125
|
+
cls: this.cls("whats-new-content"),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const markdownContent = formatChangelogSections(changelogSections);
|
|
129
|
+
|
|
130
|
+
await MarkdownRenderer.render(
|
|
131
|
+
this.app,
|
|
132
|
+
markdownContent,
|
|
133
|
+
changelogContainer,
|
|
134
|
+
"/",
|
|
135
|
+
this.plugin
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Action buttons
|
|
140
|
+
const buttonContainer = contentEl.createDiv({
|
|
141
|
+
cls: this.cls("whats-new-buttons"),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Full changelog button (optional)
|
|
145
|
+
if (this.config.links.changelog) {
|
|
146
|
+
const changelogBtn = buttonContainer.createEl("button", {
|
|
147
|
+
text: "Full Changelog",
|
|
148
|
+
});
|
|
149
|
+
changelogBtn.addEventListener("click", () => {
|
|
150
|
+
window.open(this.config.links.changelog, "_blank");
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Documentation button (optional)
|
|
155
|
+
if (this.config.links.documentation) {
|
|
156
|
+
const docsBtn = buttonContainer.createEl("button", {
|
|
157
|
+
text: "Documentation",
|
|
158
|
+
});
|
|
159
|
+
docsBtn.addEventListener("click", () => {
|
|
160
|
+
window.open(this.config.links.documentation, "_blank");
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Close button (always present)
|
|
165
|
+
const closeBtn = buttonContainer.createEl("button", {
|
|
166
|
+
text: "Close",
|
|
167
|
+
cls: this.cls("mod-cta"),
|
|
168
|
+
});
|
|
169
|
+
closeBtn.addEventListener("click", () => this.close());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
onClose() {
|
|
173
|
+
this.contentEl.empty();
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/file/templater.ts
CHANGED
|
@@ -60,19 +60,15 @@ export function shouldUseTemplate(app: App, templatePath: string | undefined): b
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Creates a file
|
|
63
|
+
* Creates a file at the specified full path with optional frontmatter and content.
|
|
64
64
|
* Returns existing file if it already exists.
|
|
65
65
|
*/
|
|
66
|
-
export async function
|
|
66
|
+
export async function createFileAtPath(
|
|
67
67
|
app: App,
|
|
68
|
-
|
|
69
|
-
filename: string,
|
|
68
|
+
filePath: string,
|
|
70
69
|
content?: string,
|
|
71
70
|
frontmatter?: Record<string, unknown>
|
|
72
71
|
): Promise<TFile> {
|
|
73
|
-
const baseName = filename.replace(/\.md$/, "");
|
|
74
|
-
const filePath = `${targetDirectory}/${baseName}.md`;
|
|
75
|
-
|
|
76
72
|
// Check if file already exists
|
|
77
73
|
const existingFile = app.vault.getAbstractFileByPath(filePath);
|
|
78
74
|
if (existingFile instanceof TFile) {
|
|
@@ -92,6 +88,23 @@ export async function createFileManually(
|
|
|
92
88
|
return file;
|
|
93
89
|
}
|
|
94
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Creates a file manually with optional frontmatter and content.
|
|
93
|
+
* Returns existing file if it already exists.
|
|
94
|
+
*/
|
|
95
|
+
export async function createFileManually(
|
|
96
|
+
app: App,
|
|
97
|
+
targetDirectory: string,
|
|
98
|
+
filename: string,
|
|
99
|
+
content?: string,
|
|
100
|
+
frontmatter?: Record<string, unknown>
|
|
101
|
+
): Promise<TFile> {
|
|
102
|
+
const baseName = filename.replace(/\.md$/, "");
|
|
103
|
+
const filePath = `${targetDirectory}/${baseName}.md`;
|
|
104
|
+
|
|
105
|
+
return createFileAtPath(app, filePath, content, frontmatter);
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
export async function createFromTemplate(
|
|
96
109
|
app: App,
|
|
97
110
|
templatePath: string,
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
interface VersionSection {
|
|
2
|
+
version: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses changelog markdown content into version sections.
|
|
8
|
+
* Each section starts with "## X.Y.Z" heading.
|
|
9
|
+
*/
|
|
10
|
+
export function parseChangelog(changelogContent: string): VersionSection[] {
|
|
11
|
+
const sections: VersionSection[] = [];
|
|
12
|
+
const lines = changelogContent.split("\n");
|
|
13
|
+
|
|
14
|
+
let currentVersion: string | null = null;
|
|
15
|
+
let currentContent: string[] = [];
|
|
16
|
+
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
const versionMatch = line.match(/^##\s+(\d+\.\d+\.\d+)/);
|
|
19
|
+
|
|
20
|
+
if (versionMatch) {
|
|
21
|
+
if (currentVersion !== null) {
|
|
22
|
+
sections.push({
|
|
23
|
+
version: currentVersion,
|
|
24
|
+
content: currentContent.join("\n").trim(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
currentVersion = versionMatch[1];
|
|
29
|
+
currentContent = [];
|
|
30
|
+
} else if (currentVersion !== null) {
|
|
31
|
+
currentContent.push(line);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (currentVersion !== null && currentContent.length > 0) {
|
|
36
|
+
sections.push({
|
|
37
|
+
version: currentVersion,
|
|
38
|
+
content: currentContent.join("\n").trim(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return sections;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compares two semantic version strings.
|
|
47
|
+
* Returns:
|
|
48
|
+
* - negative if v1 < v2
|
|
49
|
+
* - 0 if v1 === v2
|
|
50
|
+
* - positive if v1 > v2
|
|
51
|
+
*/
|
|
52
|
+
function compareVersions(v1: string, v2: string): number {
|
|
53
|
+
const parts1 = v1.split(".").map(Number);
|
|
54
|
+
const parts2 = v2.split(".").map(Number);
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
57
|
+
const num1 = parts1[i] || 0;
|
|
58
|
+
const num2 = parts2[i] || 0;
|
|
59
|
+
|
|
60
|
+
if (num1 !== num2) {
|
|
61
|
+
return num1 - num2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets all changelog sections between fromVersion (exclusive) and toVersion (inclusive).
|
|
70
|
+
* Sections are returned in reverse chronological order (newest first).
|
|
71
|
+
*/
|
|
72
|
+
export function getChangelogSince(
|
|
73
|
+
changelogContent: string,
|
|
74
|
+
fromVersion: string,
|
|
75
|
+
toVersion: string
|
|
76
|
+
): VersionSection[] {
|
|
77
|
+
const allSections = parseChangelog(changelogContent);
|
|
78
|
+
|
|
79
|
+
return allSections.filter((section) => {
|
|
80
|
+
const isAfterFrom = compareVersions(section.version, fromVersion) > 0;
|
|
81
|
+
const isBeforeOrEqualTo = compareVersions(section.version, toVersion) <= 0;
|
|
82
|
+
return isAfterFrom && isBeforeOrEqualTo;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function formatChangelogSections(sections: VersionSection[]): string {
|
|
87
|
+
if (sections.length === 0) {
|
|
88
|
+
return "No changes found.";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return sections
|
|
92
|
+
.map((section) => {
|
|
93
|
+
// Escape Dataview inline queries to prevent parsing errors
|
|
94
|
+
const content = section.content.replace(/`=([^`]+)`/g, "`\\=$1`");
|
|
95
|
+
return `## ${section.version}\n\n${content}`;
|
|
96
|
+
})
|
|
97
|
+
.join("\n\n");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type { VersionSection };
|
package/src/string/index.ts
CHANGED