Welcome to Chapter 4!
In the previous chapter, Matcher Selection Mode, we narrowed down our search by selecting a tool name (like git).
Now, we have reached the bottom of our "Department Store" directory. We are finally looking at the shelf with the actual products. This is the Hook Selection Mode.
Think of your computer's file explorer:
We have filtered down to a specific context (e.g., "Before git runs"). Now we simply need to list every script that is scheduled to run in this context so the user can pick one to examine.
The Problem:
You have selected git. However, you might have multiple scripts that run before git.
We need to list both of these items, along with a label indicating what kind of hook they are (e.g., a simple command or a complex script).
Here is how the data flows when the user interacts with this list:
To build this, we focus on three elements:
settings.json or from an installed Plugin. We need to display this so the user knows where the setting originated.command? A prompt? We show this in brackets [] to categorize the items visually.
Let's look at SelectHookMode.tsx. This component is very similar to the previous ones, but it handles the final level of detail.
We receive a list of hook objects (hooksForSelectedMatcher). We need to convert these into options for our menu.
We want the list item to look like: [command] echo "hello".
const options = hooksForSelectedMatcher.map((hook, index) => {
return {
// Show type and a summary of the command
label: `[${hook.config.type}] ${getHookDisplayText(hook.config)}`,
// We use the index as the unique ID
value: index.toString(),
// Show if this came from a Plugin or a Local file
description: getSourceDescription(hook)
};
});
Explanation:
label: We combine the type (e.g., command) with a display text (the actual script command).value: We convert the numeric index (0, 1, 2) to a string so our generic Select component can handle it.
When the user chooses an item, we get the value back (which is the index string, like "0"). We need to convert that back into the actual hook object.
const handleSelect = (value: string) => {
// 1. Convert "0" back to number 0
const index = parseInt(value, 10);
// 2. Find the object in our list
const selectedHook = hooksForSelectedMatcher[index];
// 3. Tell the parent component we found it
if (selectedHook) {
onSelect(selectedHook);
}
};
Explanation:
parseInt to turn the ID back into a number.Ideally, if we reached this screen, there should be hooks. But if something went wrong or the list is empty, we show a helpful message.
if (hooksForSelectedMatcher.length === 0) {
return (
<Dialog title={title} onCancel={onCancel}>
<Box flexDirection="column" gap={1}>
<Text dimColor>No hooks configured for this event.</Text>
<Text dimColor>Edit settings.json to add one.</Text>
</Box>
</Dialog>
);
}
Finally, we render the dialog containing our list.
return (
<Dialog
title={title}
subtitle={hookEventMetadata.description}
onCancel={onCancel}
>
<Box flexDirection="column">
<Select
options={options}
onChange={handleSelect} // Uses the logic from Step 2
onCancel={onCancel}
/>
</Box>
</Dialog>
);
Explanation:
subtitle: We remind the user what this event does (e.g., "Runs before a tool execution").Select: Displays the list we built in Step 1.In this chapter, we built the Hook Selection Mode.
The Journey So Far:
What's Next? The user has finally picked a single item! Now they want to see the full details: the exact script command, the working directory, and other settings.
Let's move on to Chapter 5: Hook Detail View.
Generated by Code IQ