// File paths in the terminal become links (devdesk#21): click one in Claude's // output (web/src/files.js, ./notes.md:12, /root/code/x/y.py:3:9) to open it in // the Files pane, at that line if there is one. Only paths that are files of // the session become links, so words and URLs are left alone. // A path: optional ./ ../ or /, segments, and either a slash or an extension; // then an optional :line[:col]. const PATH_RE = /(? [m.index, m.index + m[0].length]); const out = []; for (const m of text.matchAll(PATH_RE)) { const candidate = m[1]; if (!candidate.includes('/') && !/\.[A-Za-z0-9]{1,10}$/.test(candidate)) continue; if (urls.some(([a, b]) => m.index >= a && m.index < b)) continue; out.push({ index: m.index, length: m[0].length, path: candidate, line: m[2] ? Number(m[2]) : undefined }); } return out; } export function registerPathLinks(term, { resolve, open }) { term.registerLinkProvider({ provideLinks(y, callback) { const line = term.buffer.active.getLine(y - 1); if (!line) { callback(undefined); return; } const links = []; for (const found of findPaths(line.translateToString(true))) { const path = resolve(found.path); if (!path) continue; links.push({ range: { start: { x: found.index + 1, y }, end: { x: found.index + found.length, y } }, text: path, decorations: { underline: true, pointerCursor: true }, activate: () => open(path, found.line), }); } callback(links.length ? links : undefined); }, }); }