Files
obsidian-global-config/plugins/multi-vault-links-0.1.4/main.js
Brendan Chen f865b400fb Initial global Obsidian config
Shared config extracted from obsidian-config; vault-specific files (bookmarks.json, daily-notes.json) excluded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01ASYabtTgxSxJPjxPmBK9PJ
2026-06-24 12:03:59 -04:00

254 lines
7.5 KiB
JavaScript

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/main.ts
var main_exports = {};
__export(main_exports, {
createLinkEl: () => createLinkEl,
default: () => MultiVaultLinksPlugin,
openVaultUri: () => openVaultUri
});
module.exports = __toCommonJS(main_exports);
var import_obsidian2 = require("obsidian");
// src/parse.ts
var LINK_RE = /\[\[([^\]]+?)\]\]/g;
var VAULT_RE = /^\s*([^#/|:]+?)\s*:\s*([\s\S]+)$/;
function parseInner(inner) {
const m = VAULT_RE.exec(inner);
if (!m)
return null;
const vault = m[1].trim();
let rest = m[2];
let alias = null;
const pipe = rest.indexOf("|");
if (pipe !== -1) {
alias = rest.slice(pipe + 1).trim();
rest = rest.slice(0, pipe);
}
const hash = rest.indexOf("#");
let file;
let subpath;
if (hash !== -1) {
file = rest.slice(0, hash).trim();
subpath = rest.slice(hash).trim();
} else {
file = rest.trim();
subpath = "";
}
if (!vault || !file)
return null;
const display = `${vault}: ${file}${subpath}`;
return { vault, file, subpath, alias, display };
}
function findVaultLinks(text) {
const out = [];
LINK_RE.lastIndex = 0;
let m;
while ((m = LINK_RE.exec(text)) !== null) {
const parsed = parseInner(m[1]);
if (!parsed)
continue;
out.push({
...parsed,
raw: m[0],
start: m.index,
end: m.index + m[0].length
});
}
return out;
}
function parseVaultTarget(target) {
return parseInner(target);
}
function buildObsidianUri(link) {
const vault = encodeURIComponent(link.vault);
const file = encodeURIComponent(`${link.file}${link.subpath}`);
return `obsidian://open?vault=${vault}&file=${file}`;
}
// src/livePreview.ts
var import_view = require("@codemirror/view");
var import_state = require("@codemirror/state");
var import_obsidian = require("obsidian");
var VaultLinkWidget = class extends import_view.WidgetType {
constructor(link) {
super();
this.link = link;
}
eq(other) {
return other.link.raw === this.link.raw;
}
toDOM() {
const a = document.createElement("a");
a.className = "multi-vault-link cm-multi-vault-link";
const uri = buildObsidianUri(this.link);
a.href = uri;
a.setAttribute("aria-label", `${this.link.vault} \u2192 ${this.link.file}${this.link.subpath}`);
a.textContent = this.link.display;
a.addEventListener("click", (evt) => {
evt.preventDefault();
evt.stopPropagation();
window.open(uri);
});
return a;
}
ignoreEvent() {
return true;
}
};
function selectionTouches(view, from, to) {
return view.state.selection.ranges.some((r) => r.from <= to && r.to >= from);
}
function buildDecorations(view) {
const builder = new import_state.RangeSetBuilder();
const livePreview = view.state.field(import_obsidian.editorLivePreviewField);
for (const { from, to } of view.visibleRanges) {
const text = view.state.doc.sliceString(from, to);
for (const link of findVaultLinks(text)) {
const start = from + link.start;
const end = from + link.end;
if (!livePreview || selectionTouches(view, start, end)) {
builder.add(start, end, import_view.Decoration.mark({ class: "cm-multi-vault-source" }));
continue;
}
builder.add(
start,
end,
import_view.Decoration.replace({ widget: new VaultLinkWidget(link) })
);
}
}
return builder.finish();
}
var multiVaultLivePreview = import_view.ViewPlugin.fromClass(
class {
constructor(view) {
this.decorations = buildDecorations(view);
}
update(update) {
const modeChanged = update.startState.field(import_obsidian.editorLivePreviewField) !== update.state.field(import_obsidian.editorLivePreviewField);
if (update.docChanged || update.viewportChanged || update.selectionSet || modeChanged) {
this.decorations = buildDecorations(update.view);
}
}
},
{ decorations: (v) => v.decorations }
);
// src/main.ts
var SKIP_TAGS = /* @__PURE__ */ new Set(["CODE", "PRE", "A"]);
function isInsideSkipped(node) {
let el = node.parentElement;
while (el) {
if (SKIP_TAGS.has(el.tagName))
return true;
el = el.parentElement;
}
return false;
}
function openVaultUri(uri, win = window) {
win.open(uri);
}
function attachOpenHandler(a, uri) {
a.addEventListener("click", (evt) => {
evt.preventDefault();
evt.stopPropagation();
openVaultUri(uri, a.ownerDocument.defaultView ?? window);
});
}
function buildAnchor(doc, link, display) {
const a = doc.createElement("a");
a.addClass("multi-vault-link");
const uri = buildObsidianUri(link);
a.setAttribute("href", uri);
a.setAttribute("aria-label", `${link.vault} \u2192 ${link.file}${link.subpath}`);
a.textContent = display;
attachOpenHandler(a, uri);
return a;
}
function createLinkEl(doc, link) {
return buildAnchor(doc, link, link.display);
}
function rewriteInternalLinks(el) {
el.querySelectorAll("a.internal-link").forEach((a) => {
const target = a.getAttribute("data-href") ?? a.getAttribute("href") ?? "";
const link = parseVaultTarget(target);
if (!link)
return;
a.replaceWith(buildAnchor(a.ownerDocument, link, link.display));
});
}
function rewriteTextNode(node) {
const text = node.nodeValue ?? "";
const links = findVaultLinks(text);
if (links.length === 0)
return;
const doc = node.ownerDocument;
const frag = doc.createDocumentFragment();
let cursor = 0;
for (const link of links) {
if (link.start > cursor) {
frag.appendChild(doc.createTextNode(text.slice(cursor, link.start)));
}
frag.appendChild(createLinkEl(doc, link));
cursor = link.end;
}
if (cursor < text.length) {
frag.appendChild(doc.createTextNode(text.slice(cursor)));
}
node.replaceWith(frag);
}
var MultiVaultLinksPlugin = class extends import_obsidian2.Plugin {
async onload() {
this.registerMarkdownPostProcessor((el) => {
rewriteInternalLinks(el);
const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);
const targets = [];
let n;
while (n = walker.nextNode()) {
const t = n;
if (!t.nodeValue || !t.nodeValue.includes("[["))
continue;
if (isInsideSkipped(t))
continue;
targets.push(t);
}
targets.forEach(rewriteTextNode);
});
this.registerEditorExtension(multiVaultLivePreview);
this.patchOpenLinkText();
}
patchOpenLinkText() {
const ws = this.app.workspace;
const original = ws.openLinkText.bind(ws);
ws.openLinkText = (linktext, sourcePath, newLeaf, viewState) => {
const link = parseVaultTarget(linktext);
if (link) {
openVaultUri(buildObsidianUri(link));
return Promise.resolve();
}
return original(linktext, sourcePath, newLeaf, viewState);
};
this.register(() => {
ws.openLinkText = original;
});
}
};