@real1ty-obsidian-plugins/utils 2.24.0 → 2.25.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.
@@ -51,6 +51,12 @@ export declare class WhatsNewModal extends Modal {
51
51
  * Helper to add CSS class to an element.
52
52
  */
53
53
  private addCls;
54
+ /**
55
+ * Makes external links in rendered markdown clickable by adding click handlers.
56
+ * Finds all anchor tags with external URLs (http/https) and adds click events
57
+ * that open the links in the user's default browser.
58
+ */
59
+ private makeExternalLinksClickable;
54
60
  onOpen(): Promise<void>;
55
61
  onClose(): void;
56
62
  }
@@ -1 +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"}
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;IAId;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAoB5B,MAAM;IAsGZ,OAAO;CAGP"}
@@ -25,6 +25,27 @@ export class WhatsNewModal extends Modal {
25
25
  addCls(el, suffix) {
26
26
  el.classList.add(this.cls(suffix));
27
27
  }
28
+ /**
29
+ * Makes external links in rendered markdown clickable by adding click handlers.
30
+ * Finds all anchor tags with external URLs (http/https) and adds click events
31
+ * that open the links in the user's default browser.
32
+ */
33
+ makeExternalLinksClickable(container) {
34
+ const links = container.querySelectorAll("a[href]");
35
+ // Convert NodeList to Array for iteration
36
+ Array.from(links).forEach((link) => {
37
+ const href = link.getAttribute("href");
38
+ // Only handle external HTTP(S) links
39
+ if (href && (href.startsWith("http://") || href.startsWith("https://"))) {
40
+ link.addEventListener("click", (event) => {
41
+ event.preventDefault();
42
+ window.open(href, "_blank");
43
+ });
44
+ // Add visual indicator that it's an external link
45
+ link.addClass("external-link");
46
+ }
47
+ });
48
+ }
28
49
  onOpen() {
29
50
  return __awaiter(this, void 0, void 0, function* () {
30
51
  const { contentEl } = this;
@@ -70,6 +91,8 @@ export class WhatsNewModal extends Modal {
70
91
  });
71
92
  const markdownContent = formatChangelogSections(changelogSections);
72
93
  yield MarkdownRenderer.render(this.app, markdownContent, changelogContainer, "/", this.plugin);
94
+ // Make external links clickable
95
+ this.makeExternalLinksClickable(changelogContainer);
73
96
  }
74
97
  // Action buttons
75
98
  const buttonContainer = contentEl.createDiv({
@@ -1 +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"]}
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\t/**\n\t * Makes external links in rendered markdown clickable by adding click handlers.\n\t * Finds all anchor tags with external URLs (http/https) and adds click events\n\t * that open the links in the user's default browser.\n\t */\n\tprivate makeExternalLinksClickable(container: HTMLElement): void {\n\t\tconst links = container.querySelectorAll<HTMLAnchorElement>(\"a[href]\");\n\n\t\t// Convert NodeList to Array for iteration\n\t\tArray.from(links).forEach((link) => {\n\t\t\tconst href = link.getAttribute(\"href\");\n\n\t\t\t// Only handle external HTTP(S) links\n\t\t\tif (href && (href.startsWith(\"http://\") || href.startsWith(\"https://\"))) {\n\t\t\t\tlink.addEventListener(\"click\", (event: MouseEvent) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\twindow.open(href, \"_blank\");\n\t\t\t\t});\n\n\t\t\t\t// Add visual indicator that it's an external link\n\t\t\t\tlink.addClass(\"external-link\");\n\t\t\t}\n\t\t});\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\n\t\t\t// Make external links clickable\n\t\t\tthis.makeExternalLinksClickable(changelogContainer);\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"]}
@@ -1,12 +1,4 @@
1
1
  import type { App } from "obsidian";
2
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
3
  export declare function applyFrontmatterChanges(app: App, targetPath: string, sourceFrontmatter: Frontmatter, diff: FrontmatterDiff): Promise<void>;
12
4
  //# sourceMappingURL=frontmatter-propagation.d.ts.map
@@ -1 +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"}
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,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,WAAW,EAC9B,IAAI,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -1,19 +1,5 @@
1
1
  import { __awaiter } from "tslib";
2
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
3
  export function applyFrontmatterChanges(app, targetPath, sourceFrontmatter, diff) {
18
4
  return __awaiter(this, void 0, void 0, function* () {
19
5
  try {
@@ -1 +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"]}
1
+ {"version":3,"file":"frontmatter-propagation.js","sourceRoot":"","sources":["../../src/file/frontmatter-propagation.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAGjC,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 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real1ty-obsidian-plugins/utils",
3
- "version": "2.24.0",
3
+ "version": "2.25.0",
4
4
  "description": "Shared utilities for Obsidian plugins",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -70,6 +70,31 @@ export class WhatsNewModal extends Modal {
70
70
  el.classList.add(this.cls(suffix));
71
71
  }
72
72
 
73
+ /**
74
+ * Makes external links in rendered markdown clickable by adding click handlers.
75
+ * Finds all anchor tags with external URLs (http/https) and adds click events
76
+ * that open the links in the user's default browser.
77
+ */
78
+ private makeExternalLinksClickable(container: HTMLElement): void {
79
+ const links = container.querySelectorAll<HTMLAnchorElement>("a[href]");
80
+
81
+ // Convert NodeList to Array for iteration
82
+ Array.from(links).forEach((link) => {
83
+ const href = link.getAttribute("href");
84
+
85
+ // Only handle external HTTP(S) links
86
+ if (href && (href.startsWith("http://") || href.startsWith("https://"))) {
87
+ link.addEventListener("click", (event: MouseEvent) => {
88
+ event.preventDefault();
89
+ window.open(href, "_blank");
90
+ });
91
+
92
+ // Add visual indicator that it's an external link
93
+ link.addClass("external-link");
94
+ }
95
+ });
96
+ }
97
+
73
98
  async onOpen() {
74
99
  const { contentEl } = this;
75
100
  contentEl.empty();
@@ -134,6 +159,9 @@ export class WhatsNewModal extends Modal {
134
159
  "/",
135
160
  this.plugin
136
161
  );
162
+
163
+ // Make external links clickable
164
+ this.makeExternalLinksClickable(changelogContainer);
137
165
  }
138
166
 
139
167
  // Action buttons
@@ -2,31 +2,6 @@ import type { App } from "obsidian";
2
2
  import { TFile } from "obsidian";
3
3
  import type { Frontmatter, FrontmatterDiff } from "./frontmatter-diff";
4
4
 
5
- export interface NexusPropertiesSettings {
6
- excludedPropagatedProps?: string;
7
- parentProp: string;
8
- childrenProp: string;
9
- relatedProp: string;
10
- zettelIdProp: string;
11
- }
12
-
13
- export function parseExcludedProps(settings: NexusPropertiesSettings): Set<string> {
14
- const excludedPropsStr = settings.excludedPropagatedProps || "";
15
- const userExcluded = excludedPropsStr
16
- .split(",")
17
- .map((prop) => prop.trim())
18
- .filter((prop) => prop.length > 0);
19
-
20
- const alwaysExcluded = [
21
- settings.parentProp,
22
- settings.childrenProp,
23
- settings.relatedProp,
24
- settings.zettelIdProp,
25
- ];
26
-
27
- return new Set([...alwaysExcluded, ...userExcluded]);
28
- }
29
-
30
5
  export async function applyFrontmatterChanges(
31
6
  app: App,
32
7
  targetPath: string,