Provides mouse support for Ink components.
simplescreenrecorder-2024-06-26_00.29.29.mp4
- Mouse position tracking
- Mouse hover tracking
- Mouse click tracking
- Mouse drag tracking
- Element position tracking
Note
Without useInput
, escape sequence characters will being printed to the terminal during mouse movements.
import React, { useMemo, useRef, useState } from 'react';
import type { ComponentProps } from 'react';
import { Box, DOMElement, Text, render, useInput } from 'ink';
import {
MouseProvider,
useOnMouseHover,
useMousePosition,
useOnMouseClick,
useMouse,
} from '@zenobius/ink-mouse';
import { useMap } from '@react-hookz/web';
function App() {
return (
<MouseProvider>
<MyComponent />
</MouseProvider>
);
}
function MyComponent() {
const mouse = useMouse();
const mousePosition = useMousePosition();
const map = useMap<'button1', number>() // Example of a simple state map
/**
* Without this, your terminal will fill up with escape codes when you move the mouse.
*/
useInput((input, key) => {
if (key.return) {
mouse.toggle()
}
});
return (
<Box gap={1} flexDirection='column' width={40} height={10} borderStyle="round" padding={1}>
<Box gap={1}>
<Button label="Button 1" onClick={() => map.set('button1', (map.get('button1') || 0) + 1)} />
</Box>
<Box flexDirection="column" gap={1}>
<Text>{JSON.stringify(mousePosition)}</Text>
<Text>Button 1 clicked: {map.get('button1') || 0} times</Text>
</Box>
</Box>
);
}
function Button({ label, onClick }: { label: string; onClick?: () => void }) {
const ref = useRef<DOMElement | null>(null);
const [hovering, setHovering] = useState(false);
const [clicking, setClicking] = useState(false);
useOnMouseClick(ref, (event) => {
setClicking(event);
if (event && typeof onClick === 'function') {
onClick();
}
});
useOnMouseHover(ref, setHovering);
const border = useMemo((): ComponentProps<typeof Box>['borderStyle'] => {
if (clicking) {
return 'double';
}
if (hovering) {
return 'singleDouble';
}
return 'single';
}, [clicking, hovering]);
return (
<Box
gap={1}
paddingX={1}
ref={ref}
borderStyle={border}
>
<Text>{label}</Text>
</Box>
);
}
render(<App />);
This project uses Mise and Yarn.
- Clone the repository
- Create a branch:
git checkout -b [fix|feat|docs|chore]/your-change
(see Conventional Commits) - Run
mise setup
to bootstrap the project (tooling, deps, etc) - Run
mise dev
to run the demo in watch mode - Make your changes
- Run
mise lint
to check your code - Run
mise unittest
to check your code
All project tasks are managed via Mise in .mise/tasks/
, you can list them via mise tasks
.
Note
This project does not use package.json
scripts. All tasks are defined in .mise/tasks/
to maintain consistency and prevent configuration drift.
- Support absolute elements
- Elements positioned absolutely that occupy same space as other elements will mean they both recieve click and hover events.
- Ink supports absolute positioning. I think z order is based on order rendered.
- This means to simluate knowing the z order, we might need to register the order in which elements subscribe to events?
- Support elements not rendered from 0,0
- Currently the mouse position is tracked from the top left of the terminal (0,0).
- If an element is rendered starting at (10,10) for example, the mouse position will not be accurate.
- We need to track the offset of the element and adjust the mouse position accordingly.
- Add tests.
- testing a device may be difficult; but the implementation is sufficiently abstracted from the device that it should be possible to mock the device input stream.
- stdin event stream parsing
- position tracking
- click tracking
- drag tracking
- z order priority
- testing a device may be difficult; but the implementation is sufficiently abstracted from the device that it should be possible to mock the device input stream.
- Add Drag examples
- Research Windows support
- Apparently old CMD.exe only supports rudimentary ansii escape codes for colours.
- New Windows Terminal does support ansi escape codes, so we'd have to explore what works and what doesn't.
- We might have to fall back to GPM or some other library. Seems a bit complex. want to avoid it if possible.
- Add support for right and middle click.
- I think these are supported by the terminal, but I'm not sure how to detect them. Is it lowercase M and R?