perf: optimize markdown file link detection

Only mark existing files as openable links
Avoid file-link behavior while messages stream
Limit file existence checks to 4 concurrent requests
This commit is contained in:
Bohdan Triapitsyn
2026-05-05 19:21:44 +03:00
parent fd6635a963
commit a49e2ce1ae
3 changed files with 94 additions and 21 deletions
@@ -992,10 +992,15 @@ interface MarkdownRendererProps {
disableStreamAnimation?: boolean;
variant?: MarkdownVariant;
onShowPopup?: (content: ToolPopupContent) => void;
enableFileReferences?: boolean;
}
const MERMAID_BLOCK_SELECTOR = '[data-markdown="mermaid-block"]';
const FILE_LINK_SELECTOR = '[data-openchamber-file-link="true"]';
const FILE_REFERENCE_STAT_CONCURRENCY = 4;
const FILE_REFERENCE_STAT_CACHE = new Map<string, Promise<boolean>>();
let activeFileReferenceStatCount = 0;
const pendingFileReferenceStats: Array<() => void> = [];
type ParsedFileReference = {
path: string;
@@ -1262,6 +1267,44 @@ const getResolvedReference = (rawValue: string, effectiveDirectory: string): (Pa
};
};
const fileReferenceExists = (resolvedPath: string): Promise<boolean> => {
const normalizedPath = normalizePath(resolvedPath);
if (!normalizedPath) {
return Promise.resolve(false);
}
const cached = FILE_REFERENCE_STAT_CACHE.get(normalizedPath);
if (cached) {
return cached;
}
const request = new Promise<boolean>((resolve) => {
const run = () => {
activeFileReferenceStatCount += 1;
void fetch(`/api/fs/stat?path=${encodeURIComponent(normalizedPath)}`, {
method: 'GET',
cache: 'no-store',
})
.then((response) => resolve(response.ok))
.catch(() => resolve(false))
.finally(() => {
activeFileReferenceStatCount = Math.max(0, activeFileReferenceStatCount - 1);
pendingFileReferenceStats.shift()?.();
});
};
if (activeFileReferenceStatCount < FILE_REFERENCE_STAT_CONCURRENCY) {
run();
return;
}
pendingFileReferenceStats.push(run);
});
FILE_REFERENCE_STAT_CACHE.set(normalizedPath, request);
return request;
};
const getContextDirectory = (effectiveDirectory: string, resolvedPath: string): string => {
const normalizedDirectory = normalizePath(effectiveDirectory);
if (normalizedDirectory) {
@@ -1278,11 +1321,13 @@ const useFileReferenceInteractions = ({
effectiveDirectory,
editor,
preferRuntimeEditor,
enabled,
}: {
containerRef: React.RefObject<HTMLDivElement | null>;
effectiveDirectory: string;
editor?: EditorAPI;
preferRuntimeEditor?: boolean;
enabled: boolean;
}) => {
const annotationDebounceRef = React.useRef<number | null>(null);
@@ -1291,6 +1336,20 @@ const useFileReferenceInteractions = ({
if (!container) {
return;
}
let cancelled = false;
const clearFileLinkAttributes = (candidate: HTMLElement) => {
candidate.removeAttribute('data-openchamber-file-link');
candidate.removeAttribute('data-openchamber-file-ref');
candidate.removeAttribute('data-openchamber-file-path');
if (candidate.getAttribute('title') === 'Open file') {
candidate.removeAttribute('title');
}
if (candidate.tagName.toLowerCase() !== 'a') {
candidate.removeAttribute('role');
candidate.removeAttribute('tabindex');
}
};
const annotateFileLinks = () => {
const candidates = container.querySelectorAll<HTMLElement>('[data-markdown="inline-code"], a');
@@ -1298,28 +1357,32 @@ const useFileReferenceInteractions = ({
for (const candidate of Array.from(candidates)) {
const rawCandidate = extractPathCandidateFromElement(candidate);
const resolved = getResolvedReference(rawCandidate, effectiveDirectory);
if (!resolved) {
candidate.removeAttribute('data-openchamber-file-link');
candidate.removeAttribute('data-openchamber-file-ref');
candidate.removeAttribute('data-openchamber-file-path');
if (candidate.getAttribute('title') === 'Open file') {
candidate.removeAttribute('title');
}
if (candidate.tagName.toLowerCase() !== 'a') {
candidate.removeAttribute('role');
candidate.removeAttribute('tabindex');
}
clearFileLinkAttributes(candidate);
if (!enabled || !resolved) {
continue;
}
candidate.setAttribute('data-openchamber-file-link', 'true');
candidate.setAttribute('data-openchamber-file-ref', rawCandidate);
candidate.setAttribute('data-openchamber-file-path', resolved.resolvedPath);
candidate.setAttribute('title', 'Open file');
if (candidate.tagName.toLowerCase() !== 'a') {
candidate.setAttribute('role', 'button');
candidate.setAttribute('tabindex', '0');
}
void fileReferenceExists(resolved.resolvedPath).then((exists) => {
if (cancelled || !exists || !container.contains(candidate)) {
return;
}
const latestRawCandidate = extractPathCandidateFromElement(candidate);
const latestResolved = getResolvedReference(latestRawCandidate, effectiveDirectory);
if (!latestResolved || latestResolved.resolvedPath !== resolved.resolvedPath) {
return;
}
candidate.setAttribute('data-openchamber-file-link', 'true');
candidate.setAttribute('data-openchamber-file-ref', latestRawCandidate);
candidate.setAttribute('data-openchamber-file-path', latestResolved.resolvedPath);
candidate.setAttribute('title', 'Open file');
if (candidate.tagName.toLowerCase() !== 'a') {
candidate.setAttribute('role', 'button');
candidate.setAttribute('tabindex', '0');
}
});
}
};
@@ -1416,6 +1479,7 @@ const useFileReferenceInteractions = ({
container.addEventListener('keydown', handleKeyDown);
return () => {
cancelled = true;
if (annotationDebounceRef.current !== null && typeof window !== 'undefined') {
window.clearTimeout(annotationDebounceRef.current);
}
@@ -1424,7 +1488,7 @@ const useFileReferenceInteractions = ({
container.removeEventListener('click', handleClick);
container.removeEventListener('keydown', handleKeyDown);
};
}, [containerRef, editor, effectiveDirectory, preferRuntimeEditor]);
}, [containerRef, editor, effectiveDirectory, preferRuntimeEditor, enabled]);
};
const useMermaidInlineInteractions = ({
@@ -1531,6 +1595,7 @@ const MarkdownRendererImpl: React.FC<MarkdownRendererProps> = ({
disableStreamAnimation = false,
variant = 'assistant',
onShowPopup,
enableFileReferences = true,
}) => {
const currentTheme = useCurrentMermaidTheme();
const { editor, runtime } = useRuntimeAPIs();
@@ -1543,6 +1608,7 @@ const MarkdownRendererImpl: React.FC<MarkdownRendererProps> = ({
effectiveDirectory,
editor,
preferRuntimeEditor: runtime.isVSCode,
enabled: enableFileReferences && !isStreaming,
});
useExternalLinkInteractions({ containerRef });
const openContextPreview = useUIStore((state) => state.openContextPreview);
@@ -1615,6 +1681,7 @@ const SimpleMarkdownRendererImpl: React.FC<{
onShowPopup?: (content: ToolPopupContent) => void;
mermaidControls?: MermaidControlOptions;
allowMermaidWheelZoom?: boolean;
enableFileReferences?: boolean;
}> = ({
content,
className,
@@ -1623,6 +1690,7 @@ const SimpleMarkdownRendererImpl: React.FC<{
stripFrontmatter = false,
onShowPopup,
allowMermaidWheelZoom = false,
enableFileReferences = true,
}) => {
const { editor, runtime } = useRuntimeAPIs();
const renderedContent = React.useMemo(
@@ -1644,6 +1712,7 @@ const SimpleMarkdownRendererImpl: React.FC<{
effectiveDirectory,
editor,
preferRuntimeEditor: runtime.isVSCode,
enabled: enableFileReferences,
});
useExternalLinkInteractions({ containerRef, enabled: !disableLinkSafety });
const syntaxTheme = React.useMemo(() => generateSyntaxTheme(currentTheme), [currentTheme]);
@@ -1674,5 +1743,6 @@ export const SimpleMarkdownRenderer = React.memo(SimpleMarkdownRendererImpl, (pr
&& prev.disableLinkSafety === next.disableLinkSafety
&& prev.stripFrontmatter === next.stripFrontmatter
&& prev.onShowPopup === next.onShowPopup
&& prev.allowMermaidWheelZoom === next.allowMermaidWheelZoom;
&& prev.allowMermaidWheelZoom === next.allowMermaidWheelZoom
&& prev.enableFileReferences === next.enableFileReferences;
});
@@ -84,6 +84,7 @@ const AssistantTextPart: React.FC<AssistantTextPartProps> = ({
isStreaming={isStreaming}
disableStreamAnimation={chatRenderMode === 'sorted'}
variant={part.type === 'reasoning' ? 'reasoning' : 'assistant'}
enableFileReferences={isFinalized}
/>
</div>
);
+2
View File
@@ -976,6 +976,8 @@ html:not(.dark) .chat-scroll {
cursor: pointer;
text-decoration: underline;
text-underline-offset: 2px;
user-select: text;
-webkit-user-select: text;
}
.markdown-content [data-openchamber-file-link="true"]:hover {