Skip to content

Commit 0867a0e

Browse files
Merge branch 'main' into brendan/web-http-metrics
2 parents 3bfa7a3 + 3aa8711 commit 0867a0e

8 files changed

Lines changed: 549 additions & 770 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
### Fixed
1616
- Fixed the web process being capped at a ~4GiB heap regardless of how much memory the container has, which caused multi-second garbage collection pauses on larger deployments. [#1569](https://github.com/sourcebot-dev/sourcebot/pull/1569)
17+
- Upgraded `@sentry/*` to `^10.70.0`, fixing memory leaks where spans retained request data indefinitely. [#1572](https://github.com/sourcebot-dev/sourcebot/pull/1572)
18+
- Fixed code search result links occasionally getting stuck during navigation and restored Cmd/Ctrl-click to open matches in preview. [#1574](https://github.com/sourcebot-dev/sourcebot/pull/1574)
1719

1820
## [5.1.6] - 2026-08-10
1921

packages/backend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
"@octokit/app": "^16.1.1",
2828
"@octokit/rest": "^21.0.2",
2929
"@sentry/cli": "^2.42.2",
30-
"@sentry/node": "^10.40.0",
31-
"@sentry/profiling-node": "^10.40.0",
30+
"@sentry/node": "^10.70.0",
31+
"@sentry/profiling-node": "^10.70.0",
3232
"@sourcebot/db": "workspace:*",
3333
"@sourcebot/schemas": "workspace:*",
3434
"@sourcebot/shared": "workspace:*",

packages/web/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@
102102
"@replit/codemirror-lang-solidity": "^6.0.2",
103103
"@replit/codemirror-lang-svelte": "^6.0.0",
104104
"@replit/codemirror-vim": "^6.2.1",
105-
"@sentry/nextjs": "^10.40.0",
106-
"@sentry/profiling-node": "^10.40.0",
105+
"@sentry/nextjs": "^10.70.0",
106+
"@sentry/profiling-node": "^10.70.0",
107107
"@shopify/lang-jsonc": "^1.0.0",
108108
"@sourcebot/codemirror-lang-tcl": "^1.0.13",
109109
"@sourcebot/db": "workspace:*",

packages/web/src/app/(app)/components/pathHeader.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ export const PathHeader = ({
230230

231231
<Link
232232
className={cn("font-medium cursor-pointer hover:underline", repoNameClassName)}
233+
prefetch={false}
233234
href={getBrowsePath({
234235
repoName: repo.name,
235236
path: '/',
@@ -249,6 +250,7 @@ export const PathHeader = ({
249250
>
250251
<span className="mr-0.5">@</span>
251252
<Link
253+
prefetch={false}
252254
href={getBrowsePath({
253255
repoName: repo.name,
254256
path: '',
@@ -286,6 +288,7 @@ export const PathHeader = ({
286288
<DropdownMenuContent align="start" className="min-w-[200px]">
287289
{hiddenSegments.map((segment) => (
288290
<Link
291+
prefetch={false}
289292
href={getBrowsePath({
290293
repoName: repo.name,
291294
path: segment.fullPath,
@@ -317,6 +320,7 @@ export const PathHeader = ({
317320
<VscodeFileIcon fileName={segment.name} className="h-4 w-4 mr-1 flex-shrink-0" />
318321
)}
319322
<Link
323+
prefetch={false}
320324
className={cn(
321325
"font-mono text-sm min-w-0 truncate cursor-pointer hover:underline",
322326
)}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { cleanup, createEvent, fireEvent, render, screen } from '@testing-library/react';
2+
import { afterEach, describe, expect, it, vi } from 'vitest';
3+
import { FileMatch } from './fileMatch';
4+
5+
vi.mock('next/link', () => ({
6+
default: ({ children, href, prefetch, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { prefetch?: boolean }) => (
7+
<a {...props} href="#test-target" data-href={href} data-prefetch={String(prefetch)}>
8+
{children}
9+
</a>
10+
),
11+
}));
12+
13+
vi.mock('@/app/(app)/components/lightweightCodeHighlighter', () => ({
14+
LightweightCodeHighlighter: ({ children }: { children: React.ReactNode }) => <>{children}</>,
15+
}));
16+
17+
const file = {
18+
fileName: {
19+
text: 'src/index.ts',
20+
matchRanges: [],
21+
},
22+
webUrl: '',
23+
repository: 'github.com/sourcebot-dev/sourcebot',
24+
repositoryId: 1,
25+
language: 'TypeScript',
26+
branches: ['main'],
27+
chunks: [],
28+
};
29+
30+
const match = {
31+
content: 'const result = true;',
32+
contentStart: {
33+
byteOffset: 0,
34+
lineNumber: 10,
35+
column: 1,
36+
},
37+
matchRanges: [{
38+
start: {
39+
byteOffset: 6,
40+
lineNumber: 10,
41+
column: 7,
42+
},
43+
end: {
44+
byteOffset: 12,
45+
lineNumber: 10,
46+
column: 13,
47+
},
48+
}],
49+
};
50+
51+
afterEach(() => {
52+
cleanup();
53+
});
54+
55+
describe('FileMatch', () => {
56+
it('disables prefetching and preserves ordinary link clicks', () => {
57+
const onOpenPreview = vi.fn();
58+
render(<FileMatch file={file} match={match} onOpenPreview={onOpenPreview} />);
59+
60+
const link = screen.getByRole('link');
61+
expect(link.getAttribute('data-prefetch')).toBe('false');
62+
63+
fireEvent.click(link);
64+
expect(onOpenPreview).not.toHaveBeenCalled();
65+
});
66+
67+
it.each([
68+
['Cmd', { metaKey: true }],
69+
['Ctrl', { ctrlKey: true }],
70+
])('opens the preview and cancels navigation for %s-click', (_modifier, eventInit) => {
71+
const onOpenPreview = vi.fn();
72+
render(<FileMatch file={file} match={match} onOpenPreview={onOpenPreview} />);
73+
74+
const link = screen.getByRole('link');
75+
const clickEvent = createEvent.click(link, eventInit);
76+
fireEvent(link, clickEvent);
77+
78+
expect(clickEvent.defaultPrevented).toBe(true);
79+
expect(onOpenPreview).toHaveBeenCalledOnce();
80+
});
81+
82+
it('supports modifier-plus-Enter for keyboard users', () => {
83+
const onOpenPreview = vi.fn();
84+
render(<FileMatch file={file} match={match} onOpenPreview={onOpenPreview} />);
85+
86+
const link = screen.getByRole('link');
87+
const keyDownEvent = createEvent.keyDown(link, { key: 'Enter', metaKey: true });
88+
fireEvent(link, keyDownEvent);
89+
90+
expect(keyDownEvent.defaultPrevented).toBe(true);
91+
expect(onOpenPreview).toHaveBeenCalledOnce();
92+
});
93+
});

packages/web/src/app/(app)/search/components/searchResultsPanel/fileMatch.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import { getBrowsePath } from "@/app/(app)/browse/hooks/utils";
99
interface FileMatchProps {
1010
match: SearchResultChunk;
1111
file: SearchResultFile;
12+
onOpenPreview: () => void;
1213
}
1314

1415
export const FileMatch = ({
1516
match,
1617
file,
18+
onOpenPreview,
1719
}: FileMatchProps) => {
1820
// If it's just the title, don't show a code preview
1921
if (match.matchRanges.length === 0) {
@@ -24,6 +26,7 @@ export const FileMatch = ({
2426
<Link
2527
tabIndex={0}
2628
className="cursor-pointer focus:ring-inset focus:ring-4 bg-background hover:bg-editor-lineHighlight"
29+
prefetch={false}
2730
href={getBrowsePath({
2831
repoName: file.repository,
2932
revisionName: file.branches?.[0] ?? 'HEAD',
@@ -38,6 +41,22 @@ export const FileMatch = ({
3841
}
3942
}
4043
})}
44+
onClick={(event) => {
45+
if (!event.metaKey && !event.ctrlKey) {
46+
return;
47+
}
48+
49+
event.preventDefault();
50+
onOpenPreview();
51+
}}
52+
onKeyDown={(event) => {
53+
if (event.key !== 'Enter' || (!event.metaKey && !event.ctrlKey)) {
54+
return;
55+
}
56+
57+
event.preventDefault();
58+
onOpenPreview();
59+
}}
4160
title="open file: click, open file preview: cmd/ctrl + click"
4261
>
4362
<LightweightCodeHighlighter
@@ -51,4 +70,4 @@ export const FileMatch = ({
5170
</LightweightCodeHighlighter>
5271
</Link>
5372
);
54-
}
73+
}

packages/web/src/app/(app)/search/components/searchResultsPanel/fileMatchContainer.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ export const FileMatchContainer = ({
123123
<FileMatch
124124
match={match}
125125
file={file}
126+
onOpenPreview={() => {
127+
const matchIndex = matches.slice(0, index).reduce((acc, previousMatch) => {
128+
return acc + previousMatch.matchRanges.length;
129+
}, 0);
130+
onOpenFilePreview(matchIndex);
131+
}}
126132
/>
127133
{(index !== matches.length - 1 || isMoreContentButtonVisible) && (
128134
<Separator className="bg-accent" />
@@ -153,4 +159,4 @@ export const FileMatchContainer = ({
153159
)}
154160
</div>
155161
);
156-
}
162+
}

0 commit comments

Comments
 (0)