Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

Overview

Adds support for customizable F1-F10 macro keys with visual editing UI.

Features

  • F-Key Bar: Thin bar above workspace title showing F1-F10 buttons
  • Click to Edit: Click any F-key button to configure its macro message
  • Instant Activation: Press F-keys from anywhere to send configured messages
  • Slash Command Support: Macros work with slash commands like /edit, /compact, etc.
  • Persistent Storage: Configuration saved to ~/.cmux/keybinds.jsonc

UI Components

  • FKeyBar: Displays F1-F10 buttons with message previews
    • Empty buttons show only key label in muted style
    • Configured buttons show "F1 message preview..."
    • Hover tooltips display full messages
  • EditKeybindModal: Modal dialog for editing macros
    • Textarea for message input
    • Save/Clear/Cancel buttons
    • Ctrl+Enter keyboard shortcut

Backend

  • KeybindService: Handles loading/saving keybinds.jsonc
    • JSONC format with helpful comments
    • Atomic writes to prevent corruption
    • Validation filters invalid keybinds
  • Full IPC plumbing: keybinds:get and keybinds:set channels
  • Type-safe throughout with TypeScript

Testing

  • ✅ 8 unit tests for KeybindService (all passing)
  • ✅ Type checking passes for both main and renderer
  • ✅ Build successful

Example Usage

  1. Click F1 button in the F-key bar
  2. Enter message: continue or /edit Add tests
  3. Save
  4. Press F1 from anywhere to send the message instantly

File Structure

Configuration stored in ~/.cmux/keybinds.jsonc:

// Cmux F-Key Keybinds Configuration
[
  { "key": "F1", "action": { "type": "send_message", "message": "continue" } },
  { "key": "F2", "action": { "type": "send_message", "message": "/edit Add tests" } }
]

Architecture

  • Global keybinds (not per-workspace) for simplicity
  • Messages go through ChatInputAPI for full command parsing
  • F-keys disabled while edit modal is open
  • Union type for actions makes adding new action types trivial

Implementation Details

Files Created:

  • src/types/keybinds.ts
  • src/services/keybindService.ts + tests
  • src/components/FKeyBar.tsx
  • src/components/EditKeybindModal.tsx
  • src/hooks/useFKeyBinds.ts

Files Modified:

  • src/components/AIView.tsx - Integrated FKeyBar and modal
  • src/components/ChatInput.tsx - Extended API with sendMessage()
  • src/services/ipcMain.ts - Added keybind handlers
  • src/constants/ipc-constants.ts - Added channels
  • src/preload.ts - Exposed keybinds API
  • src/types/ipc.ts - Added to IPCApi interface

Generated with cmux

- Add F1-F10 keybind bar above workspace title
- Click F-key buttons to edit macro messages
- Press F-keys to send messages (supports slash commands)
- Configuration stored in ~/.cmux/keybinds.jsonc
- Full IPC plumbing with type-safe backend service
- 8 unit tests for keybind service (all passing)

Backend:
- KeybindService for loading/saving keybinds.jsonc
- IPC handlers for get/set operations
- Type definitions for keybind actions

Frontend:
- FKeyBar component showing F1-F10 buttons
- EditKeybindModal for configuring macros
- useFKeyBinds hook for global keyboard handling
- Extended ChatInputAPI with sendMessage() method

Generated with `cmux`
- Reduced vertical spacing (3px padding instead of 4px)
- Added gradient background for depth
- Added subtle shadow and bottom border for 3D effect
- Buttons raise on hover, depress on click
- Smaller font size (10px) for compact look
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 939 to 955
// Programmatically send a message (for F-key macros, etc.)
const sendMessageProgrammatically = useCallback(
(message: string) => {
if (!message.trim() || disabled || isSending || isCompacting) {
return;
}

// Set the input value and let the regular send flow handle it
setInput(message);
// Focus the input to show what's being sent
if (inputRef.current) {
inputRef.current.focus();
}
// Trigger send after a brief delay to ensure state updates
setTimeout(() => {
void handleSend();
}, 10);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Programmatic send uses stale input state

The new sendMessage helper inserts the macro text and then triggers handleSend via a delayed callback, but handleSend captures the input value from the previous render. When the timeout fires, the closure still sees the old (often empty) input and returns immediately, leaving the macro message sitting in the input field instead of being sent. This means F‑key macros don’t actually transmit anything unless the user manually hits Enter afterward. The helper needs to send the message without relying on the stale closure—e.g. by passing the text directly into the send routine or by using a ref to a stable sender.

Useful? React with 👍 / 👎.

- Rename ModalContent to KeybindModalContent to avoid conflict with Modal's built-in ModalContent
- Remove min-width: 500px (Modal already handles responsive width)
- Fixes textarea overflow issue
- If chat input has text: append macro message with space separator
- If chat input is empty: set message and auto-send immediately
- Move cursor to end after appending for easy editing
- Directly invoke send logic instead of relying on setState + handleSend
- Duplicate minimal send logic to avoid state timing issues
- Handles all command types (/clear, /truncate, /compact, etc.)
- Auto-send now works reliably when input is empty
- Refactored handleSend to accept optional messageOverride parameter
- Added clearInputIfNeeded helper to avoid clearing on programmatic sends
- Simplified sendMessageProgrammatically from 150+ lines to ~25 lines
- Eliminated complete duplication of command parsing and send logic
- Single source of truth for all message sending

This fixes the messiness from having duplicate send logic in two places.
- Positioned FKeyBar directly above ChatInput textarea
- Makes more sense since F-keys interact with chat input
- Adjusted styling: removed bottom border, adjusted padding
- Background matches chat input section for seamless integration
- Changed background from #252526 to #1e1e1e
- Now blends seamlessly with chat output area
- Added onFocusChange prop to ChatInput component
- Track focus state in AIView with chatInputFocused
- Conditionally render FKeyBar only when input is focused
- Added smooth slide-down animation (0.2s ease-out)
- Cleaner UI: F-keys only visible when relevant
@ammar-agent ammar-agent marked this pull request as draft October 17, 2025 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant