mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
Merge pull request #665: fix(response-viewer): open markdown links to file paths in the preview, not as a same-origin URL
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
b4275e690e
6 files changed
+129
-5
No files matched your search
@@ -2967,6 +2967,12 @@ class CodemanApp {
|
||||
* output. Subtrees already inside an `<a>` are skipped so an autolinked URL
|
||||
* is never re-cut, and the anchor's textContent is the path verbatim, so
|
||||
* "copy code" still yields exactly what the agent printed.
|
||||
*
|
||||
* Before that walk it also rewrites an existing anchor whose href is a path
|
||||
* (no scheme, not `//host`, not a bare `#fragment`) into the same `a.rv-path`
|
||||
* shape, on purpose WITHOUT `FILE_PATH_LINK_PATTERN`'s root and extension
|
||||
* filter: the agent chose that href as a link target, and left alone it opens
|
||||
* as a same-origin URL (a 404 page in a new tab).
|
||||
*/
|
||||
_linkifyFilePaths(root) {
|
||||
if (!root || typeof document === 'undefined') return;
|
||||
@@ -2974,6 +2980,36 @@ class CodemanApp {
|
||||
// out of the middle of rendering a message.
|
||||
if (typeof findFilePathLinks !== 'function') return;
|
||||
|
||||
// A markdown link whose destination is a path (`[report](/home/me/run/x.json)`,
|
||||
// Codex's usual way of citing a file) resolved against Codeman's own origin
|
||||
// and opened a 404 in a new tab. Give it the linkified-path shape instead;
|
||||
// links with a scheme or `//host` keep their own behaviour.
|
||||
for (const a of root.querySelectorAll('a[href]')) {
|
||||
// The URL parser drops tabs and newlines, so `http	://` is still http.
|
||||
const href = (a.getAttribute('href') || '').replace(/[\t\n\r]/g, '');
|
||||
if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//')) continue;
|
||||
// `#L42`, `?plain=1` and a `:42:7` suffix name a spot in the file, not the
|
||||
// file. The suffix is cut before decoding, so an encoded `%3A` stays a name.
|
||||
let path = href.split('#')[0].split('?')[0].replace(/:\d+(?::\d+)?$/, '');
|
||||
try {
|
||||
path = decodeURIComponent(path);
|
||||
} catch {
|
||||
/* malformed escape: keep the path as written */
|
||||
}
|
||||
// A bare `#fragment` (and every `.rv-path`, whose href is `#`) ends up empty.
|
||||
if (!path) continue;
|
||||
// rv-path-link keeps the label in the prose font (styles.css): it is a label,
|
||||
// not a path, and break-all would split it mid-word on a phone.
|
||||
a.className = 'rv-path rv-path-link';
|
||||
a.dataset.path = path;
|
||||
a.dataset.linkified = '1';
|
||||
if (path.endsWith('/') || findFilePathLinks(path).some((f) => f.folder && f.path === path)) a.dataset.folder = '1';
|
||||
a.title = path;
|
||||
a.setAttribute('href', '#');
|
||||
a.removeAttribute('target');
|
||||
a.removeAttribute('rel');
|
||||
}
|
||||
|
||||
// Collect first: replacing a node while the walker is positioned on it
|
||||
// invalidates the traversal.
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
||||
|
||||
@@ -14961,6 +14961,14 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* A markdown link whose target is a path (`[Our incident record](/tmp/x.json)`)
|
||||
takes the rv-path shape for the click delegate, but its text is a label, not a
|
||||
path: keep it in the prose font and never break it mid-word. */
|
||||
.rv-text a.rv-path.rv-path-link {
|
||||
font: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
/* Tables — scroll wrapper keeps table proper while allowing horizontal overflow */
|
||||
.rv-table-wrap {
|
||||
margin: 1em 0;
|
||||
|
||||
Reference in new issue
Block a user