Download src/hooks/useIdeSelection.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 4.35 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useIdeSelection.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useIdeSelection.ts
-
curl -L -o useIdeSelection.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useIdeSelection.ts
4.35 kB
| import { useEffect, useRef } from 'react' | |
| import { logError } from 'src/utils/log.js' | |
| import { z } from 'zod/v4' | |
| import type { | |
| ConnectedMCPServer, | |
| MCPServerConnection, | |
| } from '../services/mcp/types.js' | |
| import { getConnectedIdeClient } from '../utils/ide.js' | |
| import { lazySchema } from '../utils/lazySchema.js' | |
| export type SelectionPoint = { | |
| line: number | |
| character: number | |
| } | |
| export type SelectionData = { | |
| selection: { | |
| start: SelectionPoint | |
| end: SelectionPoint | |
| } | null | |
| text?: string | |
| filePath?: string | |
| } | |
| export type IDESelection = { | |
| lineCount: number | |
| lineStart?: number | |
| text?: string | |
| filePath?: string | |
| } | |
| // Define the selection changed notification schema | |
| const SelectionChangedSchema = lazySchema(() => | |
| z.object({ | |
| method: z.literal('selection_changed'), | |
| params: z.object({ | |
| selection: z | |
| .object({ | |
| start: z.object({ | |
| line: z.number(), | |
| character: z.number(), | |
| }), | |
| end: z.object({ | |
| line: z.number(), | |
| character: z.number(), | |
| }), | |
| }) | |
| .nullable() | |
| .optional(), | |
| text: z.string().optional(), | |
| filePath: z.string().optional(), | |
| }), | |
| }), | |
| ) | |
| /** | |
| * A hook that tracks IDE text selection information by directly registering | |
| * with MCP client notification handlers | |
| */ | |
| export function useIdeSelection( | |
| mcpClients: MCPServerConnection[], | |
| onSelect: (selection: IDESelection) => void, | |
| ): void { | |
| const handlersRegistered = useRef(false) | |
| const currentIDERef = useRef<ConnectedMCPServer | null>(null) | |
| useEffect(() => { | |
| // Find the IDE client from the MCP clients list | |
| const ideClient = getConnectedIdeClient(mcpClients) | |
| // If the IDE client changed, we need to re-register handlers. | |
| // Normalize undefined to null so the initial ref value (null) matches | |
| // "no IDE found" (undefined), avoiding spurious resets on every MCP update. | |
| if (currentIDERef.current !== (ideClient ?? null)) { | |
| handlersRegistered.current = false | |
| currentIDERef.current = ideClient || null | |
| // Reset the selection when the IDE client changes. | |
| onSelect({ | |
| lineCount: 0, | |
| lineStart: undefined, | |
| text: undefined, | |
| filePath: undefined, | |
| }) | |
| } | |
| // Skip if we've already registered handlers for the current IDE or if there's no IDE client | |
| if (handlersRegistered.current || !ideClient) { | |
| return | |
| } | |
| // Handler function for selection changes | |
| const selectionChangeHandler = (data: SelectionData) => { | |
| if (data.selection?.start && data.selection?.end) { | |
| const { start, end } = data.selection | |
| let lineCount = end.line - start.line + 1 | |
| // If on the first character of the line, do not count the line | |
| // as being selected. | |
| if (end.character === 0) { | |
| lineCount-- | |
| } | |
| const selection = { | |
| lineCount, | |
| lineStart: start.line, | |
| text: data.text, | |
| filePath: data.filePath, | |
| } | |
| onSelect(selection) | |
| } | |
| } | |
| // Register notification handler for selection_changed events | |
| ideClient.client.setNotificationHandler( | |
| SelectionChangedSchema(), | |
| notification => { | |
| if (currentIDERef.current !== ideClient) { | |
| return | |
| } | |
| try { | |
| // Get the selection data from the notification params | |
| const selectionData = notification.params | |
| // Process selection data - validate it has required properties | |
| if ( | |
| selectionData.selection && | |
| selectionData.selection.start && | |
| selectionData.selection.end | |
| ) { | |
| // Handle selection changes | |
| selectionChangeHandler(selectionData as SelectionData) | |
| } else if (selectionData.text !== undefined) { | |
| // Handle empty selection (when text is empty string) | |
| selectionChangeHandler({ | |
| selection: null, | |
| text: selectionData.text, | |
| filePath: selectionData.filePath, | |
| }) | |
| } | |
| } catch (error) { | |
| logError(error as Error) | |
| } | |
| }, | |
| ) | |
| // Mark that we've registered handlers | |
| handlersRegistered.current = true | |
| // No cleanup needed as MCP clients manage their own lifecycle | |
| }, [mcpClients, onSelect]) | |
| } | |