Welcome to the terminalSetup project! In this tutorial series, we are going to build a smart tool that automatically fixes keyboard shortcuts for different terminals (like making sure Shift+Enter actually creates a newline).
This is Chapter 1, where we start at the very beginning: Defining the Command.
Imagine you are going to a restaurant. You sit down and look at the menu.
You wouldn't want the kitchen to cook every single dish on the menu as soon as you walk in the door. That would waste food and energy! Instead, the kitchen waits until you actually order a specific dish before they start cooking it.
In programming, we face a similar problem. Our "Kitchen" (the code that actually fixes the terminal settings) is heavy. It imports many files and does complex logic. We don't want to load all that code just because the user typed --help to see the menu.
We need a way to:
This includes the command's name and description. In our tool, we want the description to be smart. If you are on a Mac using Apple Terminal, the description should mention "Option+Enter". If you are elsewhere, it should say "Shift+Enter".
Some terminals (like Kitty or Ghostty) already handle keyboard shortcuts perfectly. If a user is using one of these, they don't need our tool. We should hide this command so we don't confuse them.
This is the magic trick. Instead of importing the code immediately, we provide a function that imports the code later.
Let's look at how we define this in index.ts. We will break this down step-by-step.
First, we need to know what terminal the user is using. We also define a list of terminals that are "too cool" for us (they don't need our help).
import type { Command } from '../../commands.js'
import { env } from '../../utils/env.js'
// Terminals that natively support CSI u / Kitty keyboard protocol
const NATIVE_CSIU_TERMINALS: Record<string, string> = {
ghostty: 'Ghostty',
kitty: 'Kitty',
'iTerm.app': 'iTerm2',
WezTerm: 'WezTerm',
}
env (which we will learn more about in Terminal Capability Detection). We also list terminals like Ghostty and Kitty that handle inputs natively.
Now we create the command object. Look at how the description changes based on the environment!
const terminalSetup = {
type: 'local-jsx',
name: 'terminal-setup',
description:
env.terminal === 'Apple_Terminal'
? 'Enable Option+Enter key binding for newlines and visual bell'
: 'Install Shift+Enter key binding for newlines',
// ... more code coming
name: This is what the user types (e.g., my-app terminal-setup).description: We use a ternary operator (? :). If env.terminal is 'Apple_Terminal', we show a specific message. Otherwise, we show a generic one.Next, we decide if this command should even appear in the help list.
// ... inside terminalSetup object
isHidden: env.terminal !== null && env.terminal in NATIVE_CSIU_TERMINALS,
// ... more code coming
isHidden is a boolean (true/false).NATIVE_CSIU_TERMINALS list, isHidden becomes true.Finally, we connect the "Kitchen". We tell the CLI where to find the implementation code, but we don't import it yet.
// ... inside terminalSetup object
load: () => import('./terminalSetup.js'),
} satisfies Command
export default terminalSetup
load: This is a function. It returns a Promise that imports ./terminalSetup.js../terminalSetup.js contains the heavy logic (the Strategy Dispatcher and patching code).load() is actually called.To visualize how this works, let's trace what happens when a user runs the application.
index.ts. It checks isHidden.terminal-setup.load() function.Here is a diagram showing the difference between checking the menu and ordering the meal:
The code we wrote in this chapter (index.ts) is essentially a Router or a configuration entry. It doesn't do the work; it points to the worker.
When load() is called, it imports ./terminalSetup.js. That file is where the real action happens. That file will eventually coordinate:
By using import('./terminalSetup.js') inside a function, we are utilizing a feature called Dynamic Imports. This is standard JavaScript behavior that allows for code splittingβkeeping your startup time fast!
In this chapter, we learned how to define a command that is smart and efficient.
But wait... our command relies heavily on env.terminal to make these decisions. How does the application actually know if we are using Apple Terminal, Ghostty, or VS Code?
Find out in the next chapter!
Next Chapter: Terminal Capability Detection
Generated by Code IQ