diff --git a/registry/cell/CodeCell.tsx b/registry/cell/CodeCell.tsx index 3b6b216..7ce8b88 100644 --- a/registry/cell/CodeCell.tsx +++ b/registry/cell/CodeCell.tsx @@ -9,6 +9,7 @@ import { type CodeMirrorEditorRef, } from "@/registry/editor/codemirror-editor"; import type { SupportedLanguage } from "@/registry/editor/languages"; +import { searchHighlight } from "@/registry/editor/search-highlight"; import { CellContainer } from "./CellContainer"; import { CompactExecutionButton } from "./CompactExecutionButton"; import type { JupyterOutput } from "./OutputArea"; @@ -84,6 +85,18 @@ interface CodeCellProps { * Whether this is the last cell (affects Enter behavior) */ isLastCell?: boolean; + /** + * Search query for highlighting matches in editor and outputs + */ + searchQuery?: string; + /** + * Character offset of the active search match in the editor (-1 for none) + */ + searchActiveOffset?: number; + /** + * Callback when search match count changes (from outputs) + */ + onSearchMatchCount?: (count: number) => void; /** * Additional CodeMirror extensions */ @@ -147,6 +160,9 @@ export function CodeCell({ onInsertCellAfter, onFormat, isLastCell = false, + searchQuery, + searchActiveOffset = -1, + onSearchMatchCount, extensions, className, }: CodeCellProps) { @@ -199,6 +215,15 @@ export function CodeCell({ [navigationKeyMap], ); + // Combine user extensions with search highlighting + const editorExtensions = useMemo( + () => [ + ...(extensions ?? []), + ...searchHighlight(searchQuery ?? "", searchActiveOffset), + ], + [extensions, searchQuery, searchActiveOffset], + ); + const handleExecute = useCallback(() => { onExecute?.(); }, [onExecute]); @@ -240,14 +265,21 @@ export function CodeCell({ language={language} onValueChange={onUpdateSource} keyMap={keyMap} - extensions={extensions} + extensions={editorExtensions} placeholder="Enter code..." className="min-h-[2rem]" autoFocus={isFocused} /> } - outputContent={} + outputContent={ + + } hideOutput={cell.outputs.length === 0} /> ); diff --git a/registry/cell/MarkdownCell.tsx b/registry/cell/MarkdownCell.tsx index 2a95d15..96f73de 100644 --- a/registry/cell/MarkdownCell.tsx +++ b/registry/cell/MarkdownCell.tsx @@ -9,6 +9,7 @@ import { CodeMirrorEditor, type CodeMirrorEditorRef, } from "@/registry/editor/codemirror-editor"; +import { searchHighlight } from "@/registry/editor/search-highlight"; import { IsolatedFrame, type IsolatedFrameHandle, @@ -63,6 +64,10 @@ interface MarkdownCellProps { * Whether this is the last cell (affects Enter behavior) */ isLastCell?: boolean; + /** + * Search query for highlighting matches in editor (edit mode) and rendered content (view mode) + */ + searchQuery?: string; /** * Additional class name for the container */ @@ -114,6 +119,7 @@ export function MarkdownCell({ onFocusNext, onInsertCellAfter, isLastCell = false, + searchQuery, className, }: MarkdownCellProps) { // Start in edit mode if cell is empty @@ -252,6 +258,19 @@ export function MarkdownCell({ [navigationKeyMap, cell.source], ); + // Search highlighting for edit mode + const editorExtensions = useMemo( + () => searchHighlight(searchQuery ?? ""), + [searchQuery], + ); + + // Sync search to iframe when searchQuery changes (view mode) + useEffect(() => { + if (!editing && frameRef.current?.isReady) { + frameRef.current.search(searchQuery ?? ""); + } + }, [searchQuery, editing]); + // Focus editor when entering edit mode (after initial mount) const initialMountRef = useRef(true); useEffect(() => { @@ -309,6 +328,7 @@ export function MarkdownCell({ onValueChange={onUpdateSource} onBlur={handleBlur} keyMap={keyMap} + extensions={editorExtensions} placeholder="Enter markdown..." className="min-h-[2rem]" autoFocus={editing}