Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ Write PR bodies for **busy reviewers**. Be concise and avoid redundancy:
- **If it's obvious, omit it** - Problem obvious from solution? Don't state it. Solution obvious from problem? Skip to implementation details.

❌ **Bad** (redundant):

```
Problem: Markdown rendering is slow, causing 50ms tasks
Solution: Make markdown rendering faster
Impact: Reduces task time to <16ms
```

✅ **Good** (each section adds value):

```
ReactMarkdown was re-parsing content on every parent render because plugin arrays
were created fresh each time. Moved to module scope for stable references.
Expand All @@ -104,8 +106,8 @@ Verify with React DevTools Profiler - MarkdownCore should only re-render when co
- User docs are built with mdbook and deployed to https://cmux.io
- Must be added to `docs/SUMMARY.md` to appear in the docs
- Use standard markdown + mermaid diagrams
- **Developer docs** → inline with the code its documenting as comments. Consider them notes as notes to future Assistants to understand the logic more quickly.
**DO NOT** create standalone documentation files in the project root or random locations.
- **Developer docs** → inline with the code its documenting as comments. Consider them notes as notes to future Assistants to understand the logic more quickly.
**DO NOT** create standalone documentation files in the project root or random locations.

### External API Docs

Expand Down Expand Up @@ -162,10 +164,10 @@ This project uses **Make** as the primary build orchestrator. See `Makefile` for

### Test-Driven Development (TDD)

**TDD is the preferred development style for agents.**
**TDD is the preferred development style for agents.**

- Prefer relocated complex logic into places where they're easily tested
- E.g. pure functions in `utils` are easier to test than complex logic in a React component
- E.g. pure functions in `utils` are easier to test than complex logic in a React component
- Strive for broad coverage with minimal tests
- Prefer testing large blocks of composite logic
- Tests should be written with the end-user experience in mind
Expand Down
19 changes: 17 additions & 2 deletions docs/vim-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ Vim mode is always enabled. Press **ESC** to enter normal mode from insert mode.
## Modes

### Insert Mode (Default)

- This is the default mode when typing in the chat input
- Type normally, all characters are inserted
- Press **ESC** or **Ctrl-[** to enter normal mode

### Normal Mode

- Command mode for navigation and editing
- Indicated by "NORMAL" text above the input
- Pending commands are shown (e.g., "NORMAL d" when delete is pending)
Expand All @@ -28,12 +30,14 @@ Vim mode is always enabled. Press **ESC** to enter normal mode from insert mode.
## Navigation

### Basic Movement

- **h** - Move left one character
- **j** - Move down one line
- **k** - Move up one line
- **l** - Move right one character

### Word Movement

- **w** - Move forward to start of next word
- **W** - Move forward to start of next WORD (whitespace-separated)
- **b** - Move backward to start of previous word
Expand All @@ -42,12 +46,14 @@ Vim mode is always enabled. Press **ESC** to enter normal mode from insert mode.
- **E** - Move to end of current/next WORD

### Line Movement

- **0** - Move to beginning of line
- **$** - Move to end of line
- **Home** - Same as **0**
- **End** - Same as **$**

### Column Preservation

When moving up/down with **j**/**k**, the cursor attempts to stay in the same column position. If a line is shorter, the cursor moves to the end of that line, but will return to the original column on longer lines.

## Entering Insert Mode
Expand All @@ -62,15 +68,18 @@ When moving up/down with **j**/**k**, the cursor attempts to stay in the same co
## Editing Commands

### Simple Edits

- **x** - Delete character under cursor
- **p** - Paste after cursor
- **P** - Paste before cursor

### Undo/Redo

- **u** - Undo last change
- **Ctrl-r** - Redo

### Line Operations

- **dd** - Delete line (yank to clipboard)
- **yy** - Yank (copy) line
- **cc** - Change line (delete and enter insert mode)
Expand All @@ -80,18 +89,21 @@ When moving up/down with **j**/**k**, the cursor attempts to stay in the same co
Vim's power comes from combining operators with motions. All operators work with all motions:

### Operators

- **d** - Delete
- **c** - Change (delete and enter insert mode)
- **y** - Yank (copy)

### Motions

- **w** - To next word
- **b** - To previous word
- **e** - To end of word
- **$** - To end of line
- **0** - To beginning of line

### Examples

- **dw** - Delete to next word
- **de** - Delete to end of word
- **d$** - Delete to end of line
Expand All @@ -103,6 +115,7 @@ Vim's power comes from combining operators with motions. All operators work with
- **yy** - Yank line (doubled operator)

### Shortcuts

- **D** - Same as **d$** (delete to end of line)
- **C** - Same as **c$** (change to end of line)

Expand All @@ -111,6 +124,7 @@ Vim's power comes from combining operators with motions. All operators work with
Text objects let you operate on semantic units:

### Inner Word (iw)

- **diw** - Delete inner word (word under cursor)
- **ciw** - Change inner word
- **yiw** - Yank inner word
Expand All @@ -125,13 +139,13 @@ Text objects work from anywhere within the word - you don't need to be at the st
## Keybind Conflicts

### ESC Key

ESC is used for:

1. Exiting Vim normal mode (highest priority)
2. NOT used for canceling edits (use **Ctrl-Q** instead)
3. NOT used for interrupting streams (use **Ctrl-C** instead)



## Tips

1. **Learn operators + motions**: Instead of memorizing every command, learn the operators (d, c, y) and motions (w, b, $, 0). They combine naturally.
Expand All @@ -143,6 +157,7 @@ ESC is used for:
## Not Yet Implemented

Features that may be added in the future:

- **ge** - Backward end of word motion
- **f{char}**, **t{char}** - Find character motions
- **i"**, **i'**, **i(**, **i[**, **i{** - More text objects
Expand Down
7 changes: 6 additions & 1 deletion src/components/AIView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,12 @@ const AIViewInner: React.FC<AIViewProps> = ({
}

return (
<ChatProvider messages={messages} cmuxMessages={cmuxMessages} model={currentModel} workspaceId={workspaceId}>
<ChatProvider
messages={messages}
cmuxMessages={cmuxMessages}
model={currentModel}
workspaceId={workspaceId}
>
<ViewContainer className={className}>
<ChatArea>
<ViewHeader>
Expand Down
3 changes: 2 additions & 1 deletion src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,8 @@ export const ChatInput: React.FC<ChatInputProps> = ({
<TooltipWrapper inline>
<HelpIndicator>?</HelpIndicator>
<Tooltip className="tooltip" align="left" width="wide">
<strong>Click to edit</strong> or use {formatKeybind(KEYBINDS.OPEN_MODEL_SELECTOR)}
<strong>Click to edit</strong> or use{" "}
{formatKeybind(KEYBINDS.OPEN_MODEL_SELECTOR)}
<br />
<br />
<strong>Abbreviations:</strong>
Expand Down
14 changes: 4 additions & 10 deletions src/components/ChatMetaSidebar/CostsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,6 @@ const SectionHeader = styled.div`
margin-bottom: 12px;
`;



// Format token display - show k for thousands with 1 decimal
const formatTokens = (tokens: number) =>
tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : tokens.toLocaleString();
Expand All @@ -263,11 +261,7 @@ const formatCostWithDollar = (cost: number | undefined): string => {
* Calculate cost with elevated pricing for 1M context (200k-1M tokens)
* For tokens above 200k, use elevated pricing rates
*/
const calculateElevatedCost = (
tokens: number,
standardRate: number,
isInput: boolean
): number => {
const calculateElevatedCost = (tokens: number, standardRate: number, isInput: boolean): number => {
if (tokens <= 200_000) {
return tokens * standardRate;
}
Expand Down Expand Up @@ -408,19 +402,19 @@ export const CostsTab: React.FC = () => {
adjustedInputCost = calculateElevatedCost(
displayUsage.input.tokens,
modelStats.input_cost_per_token,
true // isInput
true // isInput
);
// Recalculate output cost with elevated pricing
adjustedOutputCost = calculateElevatedCost(
displayUsage.output.tokens,
modelStats.output_cost_per_token,
false // isOutput
false // isOutput
);
// Recalculate reasoning cost with elevated pricing
adjustedReasoningCost = calculateElevatedCost(
displayUsage.reasoning.tokens,
modelStats.output_cost_per_token,
false // isOutput
false // isOutput
);
}

Expand Down
6 changes: 1 addition & 5 deletions src/components/ChatToggles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ interface ChatTogglesProps {
children: React.ReactNode;
}

export const ChatToggles: React.FC<ChatTogglesProps> = ({
workspaceId,
modelString,
children,
}) => {
export const ChatToggles: React.FC<ChatTogglesProps> = ({ workspaceId, modelString, children }) => {
return (
<TogglesContainer>
{children}
Expand Down
27 changes: 17 additions & 10 deletions src/components/Context1MCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ const Checkbox = styled.input`
border-radius: 2px;
background: #1e1e1e;
position: relative;

&:hover {
border-color: #007acc;
}

&:checked {
background: #007acc;
border-color: #007acc;
}

&:checked::after {
content: '';
content: "";
position: absolute;
left: 3px;
top: 0px;
Expand Down Expand Up @@ -77,15 +77,22 @@ export const Context1MCheckbox: React.FC<Context1MCheckboxProps> = ({
return (
<CheckboxContainer>
<CheckboxLabel>
<Checkbox
type="checkbox"
checked={use1M}
onChange={(e) => setUse1M(e.target.checked)}
/>
<Checkbox type="checkbox" checked={use1M} onChange={(e) => setUse1M(e.target.checked)} />
1M Context
</CheckboxLabel>
<TooltipWrapper inline>
<span style={{ cursor: "help", color: "#888", fontSize: "10px", lineHeight: "1", display: "flex", alignItems: "center" }}>?</span>
<span
style={{
cursor: "help",
color: "#888",
fontSize: "10px",
lineHeight: "1",
display: "flex",
alignItems: "center",
}}
>
?
</span>
<Tooltip className="tooltip" align="center" width="auto">
Enable 1M token context window (beta feature for Claude Sonnet 4/4.5)
</Tooltip>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Messages/MarkdownCore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const REHYPE_PLUGINS = [rehypeKatex];
/**
* Core markdown rendering component that handles all markdown processing.
* This is the single source of truth for markdown configuration.
*
*
* Memoized to prevent expensive re-parsing when content hasn't changed.
*/
export const MarkdownCore = React.memo<MarkdownCoreProps>(({ content, children }) => {
Expand Down
5 changes: 4 additions & 1 deletion src/components/Messages/MarkdownRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ interface PlanMarkdownRendererProps {
className?: string;
}

export const PlanMarkdownRenderer: React.FC<PlanMarkdownRendererProps> = ({ content, className }) => {
export const PlanMarkdownRenderer: React.FC<PlanMarkdownRendererProps> = ({
content,
className,
}) => {
return (
<PlanMarkdownContainer className={className}>
<MarkdownCore content={content} />
Expand Down
5 changes: 1 addition & 4 deletions src/components/Messages/ToolMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ function isBashTool(toolName: string, args: unknown): args is BashToolArgs {
// Type guard for file_read tool
function isFileReadTool(toolName: string, args: unknown): args is FileReadToolArgs {
return (
toolName === "file_read" &&
typeof args === "object" &&
args !== null &&
"filePath" in args
toolName === "file_read" && typeof args === "object" && args !== null && "filePath" in args
);
}

Expand Down
Loading