function gitWebBase(origin) {
if (!origin) return null;
const s = String(origin).trim();
if (/^https?:\/\//i.test(s)) {
return s.replace(/^(https?:\/\/)[^@/]+@/i, '$1') .replace(/\.git\/?$/i, '')
.replace(/\/$/, '');
}
const m = s.match(/^(?:ssh:\/\/)?(?:[^@]+@)?([^:/]+)[:/](.+?)(?:\.git)?\/?$/);
return m ? `https://${m[1]}/${m[2]}` : null;
}
function gitSourceUrl(git, relPath, line) {
const base = gitWebBase(git?.origin);
if (!base || !relPath) return null;
const ref = git.commit || git.branch || 'HEAD';
const enc = relPath.split('/').map(encodeURIComponent).join('/');
const blob = /(^|\/)github\.com\//i.test(base) ? 'blob' : '-/blob'; const anchor = (line != null && Number.isFinite(+line)) ? `#L${line}` : '';
return `${base}/${blob}/${ref}/${enc}${anchor}`;
}
function nodeSourceUrl(node, level, line) {
if (!node) return null;
if (level != null && isExternalNode(node, level)) return null;
if (node.external === true) return null;
const rel = (node.id || '').replace(/^\{[^}]+\}\//, '');
if (!rel) return null;
return gitSourceUrl(activeSnap()?.git, rel, line);
}
window.nodeSourceUrl = nodeSourceUrl;
function connSourceLine(neighbourId, centralId, level) {
const edges = (activeGraph(level).edges || [])
.filter(e => e.source === neighbourId && e.target === centralId && e.line != null);
if (!edges.length) return null;
const flow = edges.find(e => edgeIsFlow(level, e.kind));
if (flow) return flow.line;
return edges.reduce((m, e) => (e.line > m.line ? e : m)).line;
}
window.connSourceLine = connSourceLine;
function absPath(idOrPath) {
const snap = activeSnap();
const m = /^\{([^}]+)\}\/(.*)$/.exec(idOrPath || '');
if (!snap || !m) return idOrPath || '';
const base = m[1] === 'target' ? (snap.target ?? snap.roots?.target) : snap.roots?.[m[1]];
return base ? `${base}/${m[2]}` : (idOrPath || '');
}