Welcome to the HelpV2 project! If you are new to building terminal user interfaces (TUIs), you are in the right place.
In this first chapter, we are going to explore the Help System Container. Before we worry about what text to show the user, we need to build the window that holds that text.
Imagine you are building a house. Before you can arrange the furniture (the text and commands), you need to build the frame, the walls, and the door.
The Help System Container (HelpV2.tsx) is that frame. It acts as a Window Manager for the help interface. It solves three specific problems:
The user is confused and types /help. We need to:
Esc to go back to work.Here is what happens when the Help System Container is activated:
Let's look at how we build this, piece by piece. We will be looking at the file HelpV2.tsx.
The container needs two things to do its job: a way to exit (onClose) and the raw data (commands) to display.
// HelpV2.tsx
type Props = {
// A function to call when the user wants to leave
onClose: (result?: string, options?: any) => void;
// The list of all available commands
commands: Command[];
};
export function HelpV2({ onClose, commands }: Props) {
// Logic goes here...
}
Explanation: This is the entry point. We accept a list of commands (the furniture) and a generic "close door" button (onClose).
A help window that takes up 100% of the screen can be annoying. Users often want to see their previous command output while reading help. We limit the height to half the screen.
// Inside HelpV2 function
const { rows, columns } = useTerminalSize();
// Calculate 50% of the screen height
const maxHeight = Math.floor(rows / 2);
// If we are inside a modal, we might handle height differently
const insideModal = useIsInsideModal();
What happens here:
useTerminalSize() acts like a measuring tape, telling us the terminal is, for example, 40 rows high.maxHeight becomes 20 rows. We strictly enforce this limit later.A good window manager knows how to close the window. We listen for specific keystrokes.
// Define the close action
const close = () => onClose("Help dialog dismissed", { display: "system" });
// Listen for the "Dismiss" keybinding (usually ESC)
useKeybinding("help:dismiss", close, { context: "Help" });
// Also listen for Ctrl+C to exit gracefully
const exitState = useExitOnCtrlCDWithKeybindings(close);
Explanation:
useKeybinding attaches an event listener. When the user hits Esc, the close function runs.onClose notifies the parent app that we are done, so it can remove this component from the screen.We don't want to dump all commands in one giant list. We organize them into Tabs.
This involves filtering the commands list. We will dive deeper into how we categorize these in the Command Categorization Strategy chapter, but here is the high-level setup:
// Separate built-in commands from user commands
const customCommands = commands.filter(
cmd => !builtinNames.has(cmd.name) && !cmd.isHidden
);
// Create the Tabs
const tabs = [
<Tab key="general" title="general"><General /></Tab>,
<Tab key="commands" title="commands">
<Commands commands={builtinCommands} ... />
</Tab>
];
Explanation:
tabs array.<General /> component (covered in General Info Panel).<Commands /> renderer (covered in Command Catalog Renderer).
Finally, we put it all together inside a Box and a Pane.
return (
<Box flexDirection="column" height={insideModal ? undefined : maxHeight}>
<Pane color="professionalBlue">
<Tabs defaultTab="general" color="professionalBlue">
{tabs}
</Tabs>
{/* Footer text regarding Exit keys goes here */}
</Pane>
</Box>
);
Explanation:
height limit we calculated earlier.The Help System Container is the skeleton of our help interface. It doesn't worry about the specific text of a command; it worries about:
Esc or Ctrl+C out.Now that we have our container built, we need to fill the first tab with content.
Next Chapter: General Info Panel
Generated by Code IQ