or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdkey-checking.mdkey-recording.mdmain-hook.mdscope-management.md

key-checking.mddocs/

0

# Real-time Key Checking

1

2

Utility function for checking if specified keys are currently pressed, enabling conditional logic based on keyboard state.

3

4

## Capabilities

5

6

### isHotkeyPressed Function

7

8

Function to check if specific keys are currently being pressed.

9

10

```typescript { .api }

11

/**

12

* Check if specified keys are currently pressed

13

* @param key - Single key string or array of key strings to check

14

* @param delimiter - Character used to separate multiple keys in a string (default: ',')

15

* @returns True if all specified keys are currently pressed

16

*/

17

function isHotkeyPressed(key: string | readonly string[], delimiter?: string): boolean;

18

```

19

20

**Usage Examples:**

21

22

```typescript

23

import { isHotkeyPressed } from 'react-hotkeys-hook';

24

25

// Check single key

26

if (isHotkeyPressed('shift')) {

27

console.log('Shift is currently pressed');

28

}

29

30

// Check key combination (all keys must be pressed)

31

if (isHotkeyPressed('ctrl+k')) {

32

console.log('Ctrl+K is currently pressed');

33

}

34

35

// Check multiple possible combinations (any can be pressed)

36

if (isHotkeyPressed(['ctrl+k', 'cmd+k'])) {

37

console.log('Either Ctrl+K or Cmd+K is pressed');

38

}

39

40

// Check with custom delimiter

41

if (isHotkeyPressed('ctrl,shift,k', ',')) {

42

console.log('Ctrl, Shift, and K are all pressed');

43

}

44

```

45

46

## Practical Examples

47

48

### Conditional UI Rendering

49

50

```typescript

51

function ContextualHelpButton() {

52

const [showHelp, setShowHelp] = useState(false);

53

54

useEffect(() => {

55

const checkKeys = () => {

56

// Show help when F1 is pressed

57

if (isHotkeyPressed('f1')) {

58

setShowHelp(true);

59

} else {

60

setShowHelp(false);

61

}

62

};

63

64

// Check key state on each frame

65

const interval = setInterval(checkKeys, 16); // ~60fps

66

67

return () => clearInterval(interval);

68

}, []);

69

70

return (

71

<div>

72

{showHelp && (

73

<div className="help-overlay">

74

<p>Help is active while F1 is held</p>

75

</div>

76

)}

77

</div>

78

);

79

}

80

```

81

82

### Advanced Modifier Key Logic

83

84

```typescript

85

function AdvancedSelector() {

86

const [selectedItems, setSelectedItems] = useState(new Set());

87

88

const handleItemClick = (itemId) => {

89

setSelectedItems(prev => {

90

const newSet = new Set(prev);

91

92

if (isHotkeyPressed('ctrl') || isHotkeyPressed('cmd')) {

93

// Multi-select: toggle item

94

if (newSet.has(itemId)) {

95

newSet.delete(itemId);

96

} else {

97

newSet.add(itemId);

98

}

99

} else if (isHotkeyPressed('shift')) {

100

// Range select: select from last to current

101

// Implementation for range selection

102

return handleRangeSelect(itemId, prev);

103

} else {

104

// Single select: replace selection

105

newSet.clear();

106

newSet.add(itemId);

107

}

108

109

return newSet;

110

});

111

};

112

113

return (

114

<div className="item-list">

115

{items.map(item => (

116

<div

117

key={item.id}

118

className={selectedItems.has(item.id) ? 'selected' : ''}

119

onClick={() => handleItemClick(item.id)}

120

>

121

{item.name}

122

</div>

123

))}

124

</div>

125

);

126

}

127

```

128

129

### Dynamic Hotkey Descriptions

130

131

```typescript

132

function HotkeyReference() {

133

const [currentModifiers, setCurrentModifiers] = useState([]);

134

135

useEffect(() => {

136

const updateModifiers = () => {

137

const modifiers = [];

138

if (isHotkeyPressed('ctrl')) modifiers.push('Ctrl');

139

if (isHotkeyPressed('shift')) modifiers.push('Shift');

140

if (isHotkeyPressed('alt')) modifiers.push('Alt');

141

if (isHotkeyPressed('meta')) modifiers.push('Cmd');

142

143

setCurrentModifiers(modifiers);

144

};

145

146

const interval = setInterval(updateModifiers, 50);

147

return () => clearInterval(interval);

148

}, []);

149

150

const getHotkeyDescription = (baseKey, description) => {

151

const modifierStr = currentModifiers.length > 0

152

? currentModifiers.join('+') + '+'

153

: '';

154

return `${modifierStr}${baseKey} - ${description}`;

155

};

156

157

return (

158

<div className="hotkey-reference">

159

<h3>Available Hotkeys</h3>

160

{currentModifiers.length > 0 && (

161

<p>Currently holding: {currentModifiers.join(', ')}</p>

162

)}

163

164

<ul>

165

<li>{getHotkeyDescription('K', 'Open command palette')}</li>

166

<li>{getHotkeyDescription('S', 'Save document')}</li>

167

<li>{getHotkeyDescription('Z', 'Undo action')}</li>

168

</ul>

169

</div>

170

);

171

}

172

```

173

174

### Game-like Input Handling

175

176

```typescript

177

function MovementController() {

178

const [position, setPosition] = useState({ x: 0, y: 0 });

179

const [isMoving, setIsMoving] = useState(false);

180

181

useEffect(() => {

182

const updateMovement = () => {

183

let deltaX = 0;

184

let deltaY = 0;

185

let moving = false;

186

187

// Basic movement

188

if (isHotkeyPressed('w') || isHotkeyPressed('arrowup')) {

189

deltaY -= 1;

190

moving = true;

191

}

192

if (isHotkeyPressed('s') || isHotkeyPressed('arrowdown')) {

193

deltaY += 1;

194

moving = true;

195

}

196

if (isHotkeyPressed('a') || isHotkeyPressed('arrowleft')) {

197

deltaX -= 1;

198

moving = true;

199

}

200

if (isHotkeyPressed('d') || isHotkeyPressed('arrowright')) {

201

deltaX += 1;

202

moving = true;

203

}

204

205

// Speed modifier

206

const speed = isHotkeyPressed('shift') ? 2 : 1;

207

208

if (moving) {

209

setPosition(prev => ({

210

x: prev.x + deltaX * speed,

211

y: prev.y + deltaY * speed

212

}));

213

}

214

215

setIsMoving(moving);

216

};

217

218

const interval = setInterval(updateMovement, 16); // 60fps

219

return () => clearInterval(interval);

220

}, []);

221

222

return (

223

<div className="movement-controller">

224

<div

225

className={`player ${isMoving ? 'moving' : ''}`}

226

style={{

227

transform: `translate(${position.x}px, ${position.y}px)`

228

}}

229

/>

230

<div className="status">

231

Position: ({position.x}, {position.y})

232

{isHotkeyPressed('shift') && ' (Fast mode)'}

233

</div>

234

</div>

235

);

236

}

237

```

238

239

### Cross-platform Key Checking

240

241

```typescript

242

function CrossPlatformActions() {

243

const handleAction = (action) => {

244

// Check for platform-specific modifier keys

245

const cmdKey = isHotkeyPressed(['cmd', 'ctrl']); // Cmd on Mac, Ctrl elsewhere

246

const optionKey = isHotkeyPressed(['alt', 'option']); // Alt/Option

247

248

if (cmdKey && isHotkeyPressed('s')) {

249

handleSave();

250

} else if (cmdKey && isHotkeyPressed('z')) {

251

handleUndo();

252

} else if (cmdKey && optionKey && isHotkeyPressed('i')) {

253

handleDevTools();

254

}

255

};

256

257

return (

258

<div>

259

<p>Cross-platform shortcuts available:</p>

260

<ul>

261

<li>Cmd/Ctrl + S: Save</li>

262

<li>Cmd/Ctrl + Z: Undo</li>

263

<li>Cmd/Ctrl + Alt/Option + I: Dev Tools</li>

264

</ul>

265

</div>

266

);

267

}

268

```

269

270

### Debugging Key States

271

272

```typescript

273

function KeyStateDebugger() {

274

const [pressedKeys, setPressedKeys] = useState([]);

275

276

useEffect(() => {

277

const commonKeys = [

278

'ctrl', 'shift', 'alt', 'meta', 'cmd',

279

'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',

280

'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

281

'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',

282

'enter', 'escape', 'space', 'tab', 'backspace'

283

];

284

285

const checkKeys = () => {

286

const pressed = commonKeys.filter(key => isHotkeyPressed(key));

287

setPressedKeys(pressed);

288

};

289

290

const interval = setInterval(checkKeys, 50);

291

return () => clearInterval(interval);

292

}, []);

293

294

return (

295

<div className="key-debugger">

296

<h3>Currently Pressed Keys</h3>

297

<div className="pressed-keys">

298

{pressedKeys.length > 0 ? (

299

pressedKeys.map(key => (

300

<span key={key} className="key-badge">{key}</span>

301

))

302

) : (

303

<span className="no-keys">No keys pressed</span>

304

)}

305

</div>

306

307

<div className="combinations">

308

<h4>Active Combinations</h4>

309

{isHotkeyPressed('ctrl+k') && <div>Ctrl+K is active</div>}

310

{isHotkeyPressed('shift+ctrl') && <div>Shift+Ctrl is active</div>}

311

{isHotkeyPressed(['cmd+s', 'ctrl+s']) && <div>Save combination is active</div>}

312

</div>

313

</div>

314

);

315

}

316

```

317

318

## Implementation Details

319

320

### Key State Tracking

321

322

The function tracks key states using a global set that is automatically maintained:

323

- Keys are added to the set on `keydown` events

324

- Keys are removed from the set on `keyup` events

325

- The set is cleared on window blur to handle edge cases

326

- Special handling for macOS Meta key behavior

327

328

### Key Normalization

329

330

All keys are normalized to lowercase and common key codes are mapped to consistent names:

331

- `esc``escape`

332

- `return``enter`

333

- Arrow keys: `left``arrowleft`, etc.

334

- Modifier keys are consistently named across platforms

335

336

### Performance Considerations

337

338

- The key state checking is very fast (O(1) for each key lookup)

339

- No event listeners are added by calling this function

340

- Safe to call frequently (e.g., in animation loops)

341

- Memory usage is minimal and constant