Skip to content

Commit d9d8674

Browse files
committed
fix(response): wire search navigation into the HTML pretty CodeMirror
HtmlEditor previously ignored search state, so hitting Enter in the SearchBox while pretty HTML was shown did nothing — match counters updated but the editor never scrolled or highlighted the hit. Add an activeMatch prop (lineIndex / start / end) and, when it changes, dispatch a CodeMirror selection + scrollIntoView(range, {y:'center', x:'center'}). ResponseViewer maps matchAll[activeMatchIndex] into that prop when rendering the HTML pretty view. Selection reuses the native read-only highlight, so no extra decoration plumbing is needed for the basic "jump to match" behaviour.
1 parent 06ab299 commit d9d8674

2 files changed

Lines changed: 52 additions & 6 deletions

File tree

apps/desktop/src/renderer/src/components/HtmlEditor.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import { useEffect, useRef } from 'react';
22
import { EditorView, basicSetup } from 'codemirror';
33
import { html } from '@codemirror/lang-html';
4-
import { EditorState } from '@codemirror/state';
4+
import { EditorSelection, EditorState } from '@codemirror/state';
55
import { oneDark } from '@codemirror/theme-one-dark';
66

7+
export interface HtmlEditorActiveMatch {
8+
lineIndex: number;
9+
start: number;
10+
end: number;
11+
}
12+
713
/**
814
* Read-only CodeMirror editor for HTML pretty view.
915
* Adapts between light and dark themes by watching the `html.dark` class on
1016
* the document element (same mechanism as the rest of the app's Tailwind theme).
17+
* When activeMatch is set, scrolls that range into view and selects it so
18+
* the browser's selection highlight doubles as the "active match" cue.
1119
*/
12-
export function HtmlEditor({ content }: { content: string }): JSX.Element {
20+
export function HtmlEditor({
21+
content,
22+
activeMatch,
23+
}: {
24+
content: string;
25+
activeMatch?: HtmlEditorActiveMatch | null;
26+
}): JSX.Element {
1327
const containerRef = useRef<HTMLDivElement | null>(null);
1428
const viewRef = useRef<EditorView | null>(null);
1529
const isDark = document.documentElement.classList.contains('dark');
@@ -69,6 +83,24 @@ export function HtmlEditor({ content }: { content: string }): JSX.Element {
6983
});
7084
}, [content]);
7185

86+
// Follow search navigation from the outer SearchBox: map {lineIndex, start, end}
87+
// into a CodeMirror range and scroll + select it.
88+
useEffect(() => {
89+
const view = viewRef.current;
90+
if (!view || !activeMatch) return;
91+
const totalLines = view.state.doc.lines;
92+
// lineIndex is 0-based; CodeMirror lines are 1-based.
93+
const lineNumber = Math.min(totalLines, Math.max(1, activeMatch.lineIndex + 1));
94+
const line = view.state.doc.line(lineNumber);
95+
const from = Math.min(line.to, line.from + activeMatch.start);
96+
const to = Math.min(line.to, line.from + activeMatch.end);
97+
const range = EditorSelection.range(from, to);
98+
view.dispatch({
99+
selection: EditorSelection.create([range]),
100+
effects: EditorView.scrollIntoView(range, { y: 'center', x: 'center' }),
101+
});
102+
}, [activeMatch]);
103+
72104
return (
73105
<div
74106
ref={containerRef}

apps/desktop/src/renderer/src/components/ResponseViewer.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -764,11 +764,25 @@ function renderBody({
764764
}
765765

766766
// HTML pretty mode — use CodeMirror for syntax-highlighted rendering.
767-
// Search is NOT highlighted inside the editor (CodeMirror has its own search
768-
// decorations which would require full editor integration; plain text search
769-
// via the SearchBox still counts matches against the raw text).
767+
// The outer SearchBox still computes matches against the raw text; when the
768+
// user navigates (Enter / Shift+Enter), we map the active match's line/char
769+
// into a CodeMirror range and let the editor scroll+select it.
770770
if (kind === 'html' && mode === 'pretty') {
771-
return <HtmlEditor content={text} />;
771+
const active = matchAll[activeMatchIndex];
772+
return (
773+
<HtmlEditor
774+
content={text}
775+
activeMatch={
776+
active
777+
? {
778+
lineIndex: active.lineIndex,
779+
start: active.start,
780+
end: active.end,
781+
}
782+
: null
783+
}
784+
/>
785+
);
772786
}
773787

774788
// Raw / pretty for all other kinds — virtualized text view.

0 commit comments

Comments
 (0)