mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
alternate buffer support (#12471)
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useStdin } from 'ink';
|
||||
import type React from 'react';
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { ESC } from '../utils/input.js';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
import {
|
||||
isIncompleteMouseSequence,
|
||||
parseMouseEvent,
|
||||
type MouseEvent,
|
||||
type MouseEventName,
|
||||
type MouseHandler,
|
||||
} from '../utils/mouse.js';
|
||||
|
||||
export type { MouseEvent, MouseEventName, MouseHandler };
|
||||
|
||||
const MAX_MOUSE_BUFFER_SIZE = 4096;
|
||||
|
||||
interface MouseContextValue {
|
||||
subscribe: (handler: MouseHandler) => void;
|
||||
unsubscribe: (handler: MouseHandler) => void;
|
||||
}
|
||||
|
||||
const MouseContext = createContext<MouseContextValue | undefined>(undefined);
|
||||
|
||||
export function useMouseContext() {
|
||||
const context = useContext(MouseContext);
|
||||
if (!context) {
|
||||
throw new Error('useMouseContext must be used within a MouseProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useMouse(handler: MouseHandler, { isActive = true } = {}) {
|
||||
const { subscribe, unsubscribe } = useMouseContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
subscribe(handler);
|
||||
return () => unsubscribe(handler);
|
||||
}, [isActive, handler, subscribe, unsubscribe]);
|
||||
}
|
||||
|
||||
export function MouseProvider({
|
||||
children,
|
||||
mouseEventsEnabled,
|
||||
debugKeystrokeLogging,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
mouseEventsEnabled?: boolean;
|
||||
debugKeystrokeLogging?: boolean;
|
||||
}) {
|
||||
const { stdin } = useStdin();
|
||||
const subscribers = useRef<Set<MouseHandler>>(new Set()).current;
|
||||
|
||||
const subscribe = useCallback(
|
||||
(handler: MouseHandler) => {
|
||||
subscribers.add(handler);
|
||||
},
|
||||
[subscribers],
|
||||
);
|
||||
|
||||
const unsubscribe = useCallback(
|
||||
(handler: MouseHandler) => {
|
||||
subscribers.delete(handler);
|
||||
},
|
||||
[subscribers],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mouseEventsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mouseBuffer = '';
|
||||
|
||||
const broadcast = (event: MouseEvent) => {
|
||||
for (const handler of subscribers) {
|
||||
handler(event);
|
||||
}
|
||||
};
|
||||
|
||||
const handleData = (data: Buffer | string) => {
|
||||
mouseBuffer += typeof data === 'string' ? data : data.toString('utf-8');
|
||||
|
||||
// Safety cap to prevent infinite buffer growth on garbage
|
||||
if (mouseBuffer.length > MAX_MOUSE_BUFFER_SIZE) {
|
||||
mouseBuffer = mouseBuffer.slice(-MAX_MOUSE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
while (mouseBuffer.length > 0) {
|
||||
const parsed = parseMouseEvent(mouseBuffer);
|
||||
|
||||
if (parsed) {
|
||||
if (debugKeystrokeLogging) {
|
||||
debugLogger.log(
|
||||
'[DEBUG] Mouse event parsed:',
|
||||
JSON.stringify(parsed.event),
|
||||
);
|
||||
}
|
||||
broadcast(parsed.event);
|
||||
mouseBuffer = mouseBuffer.slice(parsed.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isIncompleteMouseSequence(mouseBuffer)) {
|
||||
break; // Wait for more data
|
||||
}
|
||||
|
||||
// Not a valid sequence at start, and not waiting for more data.
|
||||
// Discard garbage until next possible sequence start.
|
||||
const nextEsc = mouseBuffer.indexOf(ESC, 1);
|
||||
if (nextEsc !== -1) {
|
||||
mouseBuffer = mouseBuffer.slice(nextEsc);
|
||||
// Loop continues to try parsing at new location
|
||||
} else {
|
||||
mouseBuffer = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
stdin.on('data', handleData);
|
||||
|
||||
return () => {
|
||||
stdin.removeListener('data', handleData);
|
||||
};
|
||||
}, [stdin, mouseEventsEnabled, subscribers, debugKeystrokeLogging]);
|
||||
|
||||
return (
|
||||
<MouseContext.Provider value={{ subscribe, unsubscribe }}>
|
||||
{children}
|
||||
</MouseContext.Provider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user